Comments (8)
ok so I find the solution: I should have used suspendCancellableCoroutine
from kotlinx.coroutines.
suspendCancellableCoroutine
build a SuspendableCoroutine
that exposes (among other stuff) the two methods
public fun tryResume(value: T, idempotent: Any? = null): Any?
public fun tryResumeWithException(exception: Throwable): Any?
that basically do what I want: they check the continuation state (that I cannot access directly because it is internal data), if it was already resumed.
About Firebase: for that specific API (getFileDownloadUrl) the callback is called twice almost always. There are also other Firebase APIs that behave the same way. I don't know the cause. It is a documented behavior, but I never actually understood why this should happen at all. But this is how it works.
from kotlinx.coroutines.
After first "complete event" the completion listener is unuseful therefore you should remove it.
IMHO this isn't a Continuation issue.
from kotlinx.coroutines.
Unfortunately I could not find a way to remove the listener.
from kotlinx.coroutines.
I'm curios what difference suspendCancellableCoroutine
makes for you and what is exactly the story with firebase invoking callback more than once? In what scenarios can this happen?
from kotlinx.coroutines.
Thanks. Can you point me to the place where Firebase documents multiple callback invocations, please? I want to understand the root cause of it.
from kotlinx.coroutines.
for example here
from kotlinx.coroutines.
@elizarov, This StackOverflow outlining Firebase realtime snapshot listener using Coroutines is a great example.
I've used the following pattern.
Realtime updates
The extension function awaitRealtime
has checks including verifying the state of the continuation
in order to see whether it is in isActive
state. This is important because the function is called when the user's main feed of content is updated either by a lifecycle event, refreshing the feed manually, or removing content from their feed. Without this check there will be a crash.
ExtenstionFuction.kt
suspend fun Query.awaitRealtime() = suspendCancellableCoroutine<QuerySnapshot?> { cont ->
addSnapshotListener({ value, error ->
if (error == null && cont.isActive && !value!!.isEmpty) {
cont.resume(value)
}
})
}
In order to handle errors the try
/catch
pattern is used. In a future version the Repository LiveData will be refactored to use the Flow
pattern. The ViewModel will observe the Flows and save data to the UI using the existing LiveData states.
Repository.kt
object ContentRepository {
fun getMainFeedList(scope: CoroutineScope, isRealtime: Boolean, timeframe: Timestamp) =
liveData<Lce<PagedListResult>>(scope.coroutineContext) {
val lce = this
lce.emit(Loading())
val labeledSet = HashSet<String>()
val user = getUesrCall(...)
getLabeledContent(user, timeframe, labeledSet, SAVE_COLLECTION, lce)
getLoggedInNonRealtimeContent(timeframe, labeledSet, lce)
}
// Realtime updates with 'awaitRealtime' used
private suspend fun getLabeledContent(user: CollectionReference, timeframe: Timestamp,
labeledSet: HashSet<String>,
saveCollection: String,
lce: LiveDataScope<Lce<PagedListResult>>) =
try {
val list = ArrayList<Content?>()
user.document(COLLECTIONS_DOCUMENT)
.collection(saveCollection)
.orderBy(TIMESTAMP, DESCENDING)
.whereGreaterThanOrEqualTo(TIMESTAMP, timeframe)
.awaitRealtime()?.documentChanges?.map { doc ->
doc.document.toObject(Content::class.java).let { content ->
list.add(content)
labeledSet.add(content.id)
}
}
database.contentDao().insertContentList(list)
} catch (error: FirebaseFirestoreException) {
lce.emit(Error(PagedListResult(null,
"Error retrieving user save_collection: " +
"${error.localizedMessage}")))
}
// One time updates with 'await' used
private suspend fun getLoggedInNonRealtimeContent(timeframe: Timestamp,
labeledSet: HashSet<String>,
lce: LiveDataScope<Lce<PagedListResult>>) {
try {
val list = ArrayList<Content?>()
contentEnCollection.orderBy(TIMESTAMP, DESCENDING)
.whereGreaterThanOrEqualTo(TIMESTAMP, timeframe).get().await()
.documentChanges
?.map { change -> change.document.toObject(Content::class.java) }
?.filter { content -> !labeledSet.contains(content.id) }
?.map { content -> list.add(content) }
database.contentDao().insertContentList(list)
lce.emit(Lce.Content(PagedListResult(
queryMainContentList(timeframe), "")))
} catch (error: FirebaseFirestoreException) {
lce.emit(Error(PagedListResult(
null,
CONTENT_LOGGED_IN_NON_REALTIME_ERROR +
"${error.localizedMessage}")))
}
}
}
from kotlinx.coroutines.
Related Issues (20)
- 1.9.0-RC test task:compileDemoDebugUnitTestKotlin error Suspension functions can only be called within coroutine body. HOT 1
- Improve invokeOnCompletion and invokeOnCancellation API HOT 1
- Mention `testScope.backgroundScope` in `UncompletedCoroutinesError`
- Allow `Dispatchers.Unconfined` to use the event loops as time sources
- Add optional support for Micrometer Context Propagation
- `withContext` may execute code in the wrong context if the `coroutineContext` misleads it HOT 3
- SharedFlow doesn't have same parameters as in constructor function HOT 2
- Consider deprecation cycle for `CoroutineDispatcher.invoke` HOT 6
- How to prevent a SharedFlow collect values when the activity resumes? HOT 1
- Consider discouraging `CoroutineStart.LAZY`
- Some problem about `addLast` in LockFreeTaskQueue HOT 1
- Maybe it's reasonable to recommend wrapping a `callbackFlow` initialization into `try`-`finally`? HOT 4
- Dispatcher failures may leave coroutines uncompleted HOT 1
- `DelayWithTimeoutDiagnostics` missing from R8 rules? HOT 3
- Introduce Flow.all/any/none operators HOT 3
- [WASM] JsException: Exception was thrown while running JavaScript code kotlinx.coroutines.error_$external_fun HOT 6
- Suppressed exceptions are lost during stacktrace recovery HOT 1
- `runBlocking` executes other tasks instead of its own HOT 1
- "Kotlin Compiler Error: NoClassDefFoundError for kotlin/reflect/full/KClasses during Gradle build" HOT 1
- Add argument to sample to sample at the start of a flow instead of the end? HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kotlinx.coroutines.