Skip to content

Instantly share code, notes, and snippets.

@LouisCAD
Last active July 3, 2022 11:47
Show Gist options
  • Star 44 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save LouisCAD/58d3017eedb60ce00721cb32a461980f to your computer and use it in GitHub Desktop.
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
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)
}
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) }
}
}
@shakil807g
Copy link

how to use it in activity and fragment ??

@GeoffreyMetais
Copy link

You can just call lifecycle.coroutineScope.launch { ... }

@brenoptsouza
Copy link

brenoptsouza commented Sep 15, 2018

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

@RohitSurwase
Copy link

So, is this the alternate or better approach than implementing CoroutineScope on each life-cycle aware component like Activity, Fragment and ViewModel in App?

@LouisCAD
Copy link
Author

LouisCAD commented Nov 2, 2018

@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