Stylianos Gakis
01/09/2022, 4:19 PMlifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch {
val callback = LifecycleEventObserver { _: LifecycleOwner, event: Lifecycle.Event ->
if (event == Lifecycle.Event.ON_PAUSE) {
// do something on pause
}
}
lifecycleOwner.lifecycle.addObserver(callback)
try {
awaitCancellation()
} finally {
lifecycleOwner.lifecycle.removeObserver(callback)
}
}
}
But this try/finally
with the awaitCancellation()
combination feels a bit weird. Is there a nicer way to do this in general?Adam Powell
01/09/2022, 5:12 PMsuspendCancellableCoroutine
directly, but since Lifecycle
is so strictly thread-sensitive, doing it the way you have here will save you some headaches in terms of making sure you only add/remove the observer on the main thread. Probably create a val job = Job()
and job.complete()
in your listener, and then job.join()
in your try
block.Stylianos Gakis
01/09/2022, 5:23 PMlifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch {
val job = Job()
val callback = LifecycleEventObserver { _: LifecycleOwner, event: Lifecycle.Event ->
if (event == Lifecycle.Event.ON_PAUSE) {
job.complete()
}
}
lifecycleOwner.lifecycle.addObserver(callback)
try {
job.join()
} finally {
lifecycleOwner.lifecycle.removeObserver(callback)
}
}
And if so, how would this be different from what I had done?Adam Powell
01/09/2022, 5:24 PMStylianos Gakis
01/09/2022, 5:27 PMAdam Powell
01/09/2022, 5:28 PMAdam Powell
01/09/2022, 5:29 PMrepeatOnLifecycle
call the second observer is redundantAdam Powell
01/09/2022, 5:30 PMrepeatOnLifecycle(RESUMED) {
try {
awaitCancellation()
} finally {
// do a thing when you drop from resumed to a lower state
}
}
Adam Powell
01/09/2022, 5:31 PMrepeatOnLifecycle
already cancels the block when you drop below the target stateAdam Powell
01/09/2022, 5:31 PMStylianos Gakis
01/09/2022, 5:45 PMcoroutineScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch {
try {
awaitCancellation()
} finally {
mediaPlayer?.pause()
}
}
launch {
// other stuff I'm doing after Resumed
}
}
}
And this should work perfectly fine. I have the extra launch there as I want to do more than just one thing (I’m collecting a flow) on the other launch, but I’ve seen that the block
of repeatOnLifecycle
is launched on a coroutineScope{}
so all child coroutines should just cancel properly.
I’m still trying to grasp this whole “Structured concurrency” concept but it’s looking really good and clean when I manage to understand how to use it!Adam Powell
01/09/2022, 5:49 PMAdam Powell
01/09/2022, 5:49 PMStylianos Gakis
01/09/2022, 5:53 PMNick Allen
01/09/2022, 6:10 PMlifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
coroutineContext.job.invokeOnCompletion { mediaPlayer.pause() }
}
Nick Allen
01/09/2022, 6:20 PMinvokeOnCompletion
will wait for the entire scope to finish (all the child jobs too).Stylianos Gakis
01/09/2022, 6:25 PMNick Allen
01/09/2022, 6:37 PMStylianos Gakis
01/09/2022, 6:44 PM