CoroutineScope and CoroutineContext

Scope and Context

Every coroutine runs inside a CoroutineScope. A scope holds a CoroutineContext: a small map of elements that decide how the coroutine runs.

ElementKeyPurpose
JobJoblifecycle: active, cancelled, completed; parent-child links
CoroutineDispatcherContinuationInterceptorwhich thread(s) run the code
CoroutineNameCoroutineNamename for debugging and logs
CoroutineExceptionHandlerCoroutineExceptionHandlerlast-resort handler for uncaught exceptions

Combining Context Elements

Example

Context elements are combined with +. A child inherits the parent context and can override single elements.

import kotlinx.coroutines.*

fun main() = runBlocking(CoroutineName("parent")) {
    println("Parent name: ${coroutineContext[CoroutineName]?.name}")

    launch {
        println("Child inherits: ${coroutineContext[CoroutineName]?.name}")
    }.join()

    launch(CoroutineName("child") + Dispatchers.Default) {
        val thread = Thread.currentThread().name.substringBefore("-worker")
        println("Child overrides: ${coroutineContext[CoroutineName]?.name} on $thread")
    }.join()

    val ctx = Dispatchers.IO + CoroutineName("io-task")
    println("Context has a Job? ${ctx[Job] != null}")
    println("Context name: ${ctx[CoroutineName]?.name}")
}

Output

Parent name: parent
Child inherits: parent
Child overrides: child on DefaultDispatcher
Context has a Job? false
Context name: io-task

Scopes You Will Use

ScopeWhereCancelled when
viewModelScopeAndroid ViewModelViewModel is cleared
lifecycleScopeActivity / Fragmentlifecycle is destroyed
rememberCoroutineScope()Jetpack Composecomposable leaves the composition
coroutineScope { }inside suspend functionsblock ends or fails
CoroutineScope(SupervisorJob() + Dispatchers.Default)your own long-lived classesyou call scope.cancel()
GlobalScopeavoidnever (lives as long as the app)