I listen to a flow in whenResumed, so events are only collected while app is resumed. However I would like to ensure that events sent immediately (ie. within the same non-async stack-execution) also gets collected.
init {
lifecycleScope.launchWhenResumed {
stateUpdates.collect { onUpdate(it) }
}
// Now do stuff that might trigger event, which I want to collect above
}
I know I can use a sharedFlow with a cache, and probably that is the recommended solution, but I'm think situations could occur where I don't want cached events to be sent to new collectors.
I know I can do the following, where line 1 is executed immediately, but here again line 2 is dispatched and thus executed later:
CoroutineScope(Dispatchers.Main).launch(start = CoroutineStart.UNDISPATCHED) {
// 1
lifecycle.whenResumed {
// 2
}
}
But if I add a `lifeCycle.when
CodePudding user response:
I think I found the solution. It requires yielding inside a coroutine, so other all other coroutines scheduled on the same Dispatcher (eg. main thread) has chance to run.
runBlocking {
lifecycleScope.launchWhenResumed {
stateUpdates.collect { onUpdate(it) }
}
yield()
}
And skip the runBlocking if already inside a coroutine. The above assumes we are already running on the main-thread, and also requires to be in state resumed for collect to run. So that hints at design issue, and what I really should have been doing is:
lifecycleScope.launchWhenResumed {
launch {
stateUpdates.collect { onUpdate(it) }
}
yield()
// Do my thing, that might result in state-update
}
or equivalently
lifecycleScope.launchWhenResumed {
stateUpdates.collect { onUpdate(it) }
}
lifecycleScope.launchWhenResumed {
yield()
// Do my thing, that might result in state-update
}
See Kotlin Coroutine Yield and this other question What is the purpose of coroutine yield()?
CodePudding user response:
Instead of doing this:
runBlocking {
lifecycleScope.launchWhenResumed {
stateUpdates.collect { onUpdate(it) }
}
yield()
}
Do this, which is less fragile:
lifecycleScope.launchWhenResumed {
yield()
stateUpdates.collect { onUpdate(it) }
}
It's still a bit fragile, though, because it relies on the order coroutines were submitted.
CodePudding user response:
lifecycleScope.launch {
val mutex = Mutex(locked = true)
lifecycleScope.launchWhenResumed {
stateUpdates
.onStart { mutex.unlock() }
.collect { onUpdate(it) }
}
mutex.withLock {
// Do my thing, that might result in state-update
}
}