Last active
July 3, 2022 11:47
-
-
Save LouisCAD/58d3017eedb60ce00721cb32a461980f to your computer and use it in GitHub Desktop.
CoroutineScope and Job integration with Lifecycle for Android. Meant to be used for your coroutines in lifecycle aware components. OUTDATED. See up to date implementation here: https://github.com/LouisCAD/Splitties/tree/master/modules/lifecycle-coroutines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.arch.lifecycle.GenericLifecycleObserver | |
import android.arch.lifecycle.Lifecycle | |
import android.arch.lifecycle.Lifecycle.Event.ON_DESTROY | |
import android.arch.lifecycle.LifecycleOwner | |
import kotlinx.coroutines.experimental.CoroutineScope | |
import kotlinx.coroutines.experimental.Dispatchers | |
import kotlinx.coroutines.experimental.Job | |
import kotlinx.coroutines.experimental.android.Main | |
fun Lifecycle.createJob(cancelEvent: Lifecycle.Event = ON_DESTROY): Job { | |
if(cancelEvent in forbiddenCancelEvents) { | |
throw UnsupportedOperationException("$cancelEvent is forbidden for createJob(…).") | |
} | |
return Job().also { job -> | |
if (currentState == Lifecycle.State.DESTROYED) job.cancel() | |
else addObserver(object : GenericLifecycleObserver { | |
override fun onStateChanged(source: LifecycleOwner?, event: Lifecycle.Event) { | |
if (event == cancelEvent) { | |
removeObserver(this) | |
job.cancel() | |
} | |
} | |
}) | |
} | |
} | |
private val forbiddenCancelEvents = arrayOf( | |
Lifecycle.Event.ON_ANY, | |
Lifecycle.Event.ON_CREATE, | |
Lifecycle.Event.ON_START, | |
Lifecycle.Event.ON_RESUME | |
) | |
private val lifecycleJobs = mutableMapOf<Lifecycle, Job>() | |
val Lifecycle.job: Job | |
get() = lifecycleJobs[this] ?: createJob().also { | |
if (it.isActive) { | |
lifecycleJobs[this] = it | |
it.invokeOnCompletion { _ -> lifecycleJobs -= this } | |
} | |
} | |
private val lifecycleCoroutineScopes = mutableMapOf<Lifecycle, CoroutineScope>() | |
val Lifecycle.coroutineScope: CoroutineScope | |
get() = lifecycleCoroutineScopes[this] ?: job.let { job -> | |
val newScope = CoroutineScope(job + Dispatchers.Main) | |
if (job.isActive) { | |
lifecycleCoroutineScopes[this] = newScope | |
job.invokeOnCompletion { _ -> lifecycleCoroutineScopes -= this } | |
} | |
newScope | |
} | |
inline val LifecycleOwner.coroutineScope get() = lifecycle.coroutineScope | |
fun Lifecycle.createScope(cancelEvent: Lifecycle.Event): CoroutineScope { | |
return CoroutineScope(createJob(cancelEvent) + Dispatchers.Main) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.arch.lifecycle.GenericLifecycleObserver | |
import android.arch.lifecycle.Lifecycle | |
import android.arch.lifecycle.LifecycleOwner | |
import kotlinx.coroutines.experimental.suspendCancellableCoroutine | |
suspend fun Lifecycle.awaitState(state: Lifecycle.State) { | |
if (currentState.isAtLeast(state)) return // Fast path | |
suspendCancellableCoroutine<Unit> { c -> | |
if (currentState == Lifecycle.State.DESTROYED) { // Fast path to cancellation | |
c.cancel() | |
return@suspendCancellableCoroutine | |
} | |
val observer = object : GenericLifecycleObserver { | |
override fun onStateChanged(source: LifecycleOwner?, event: Lifecycle.Event) { | |
if (currentState.isAtLeast(state)) { | |
removeObserver(this) | |
c.resume(Unit) | |
} else if (currentState == Lifecycle.State.DESTROYED) { | |
c.cancel() | |
} | |
} | |
} | |
addObserver(observer) | |
c.invokeOnCancellation { removeObserver(observer) } | |
} | |
} |
You can just call lifecycle.coroutineScope.launch { ... }
Since coroutineScope was defined as an exented property on LifecycleOwner (line 38), and both Activity and Fragment implement it, you can simply use
coroutineScope.launch{ ... }
EDIT
You may also want to use viewLifeCycleOwner.coroutineScope.launch{...}
inside your Fragments
So, is this the alternate or better approach than implementing CoroutineScope on each life-cycle aware component like Activity, Fragment and ViewModel in App?
@RohitSurwase This doesn't work with ViewModel
, but otherwise, yes, works for all lifecycle aware components and I recommend to use it instead of writing a small boilerplate everywhere.
See the linked PR for the latest implementation.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how to use it in activity and fragment ??