Introduction
Feature
Lightweight
Suspension
Multi coroutinues can be run on one thread due to support for suspension, which doesn’t block the thread where coroutine is running.
Fewer Memory Leaks
Structured Concurrency
New coroutines can only be launched in a specific CoroutineScope which delimits the lifetime of the coroutine.
Built-in cancellation support
Cancellation is propagated automatically through the running coroutine hierarchy.
Jetpack integration
Advanced Coroutine Concepts
Suspend - Resume (Stack Frame)
Kotlin uses a stack frame to manage which function is running along with any local variables. When suspending a coroutine, the current stack frame is copied and saved for later. When resuming, the stack frame is copied back from where it was saved, and the function starts running again.
Dispatchers
Assign Dispatchers
- Dispatchers.Main
- Dispatchers.IO
- Dispatchers.Default
withContext(Dispatchers.IO)
withContext() Performance
withContext() does not add extra overhead compared to an equivalent callback-based implementation. Furthermore, it’s possible to optimize withContext() calls beyond an equivalent callback-based implementation in some situations.
Start a Coroutine
async
- Starts a new coroutine and allows you to return a result with a suspend function called await
- Exception: Async expects an eventual call to await, it holds exceptions and rethrows them as part of the await call. The exception will not show in crash metrics.
launch
- Starts a new coroutine and doesn’t return the result to the caller.
Parallel Decomposition
All coroutines that are started inside a suspend function must be stopped when that function returns, so you likely need to guarantee that those coroutines finish before returning.
With structured concurrency in Kotlin, you can** define a coroutineScope** that starts one or more coroutines.
Then, using await() (for a single coroutine) or awaitAll() (for multiple coroutines), you can guarantee that these coroutines finish before returning from the function.
Summary
Build a coroutineScope with async and await them to finish
suspend fun fetchTwoDocs() = coroutineScope {
val deferredOne = async { fetchDoc(1) }
val deferredTwo = async { fetchDoc(2) }
deferredOne.await()
deferredTwo.await()
}
Coroutines concepts
CoroutineScope
A CoroutineScope keeps track of any coroutine it creates using launch or async. scope.cancel()
class ExampleClass {
...
fun exampleMethod() {
// Handle to the coroutine, you can control its lifecycle
val job = scope.launch {
// New coroutine
}
if (...) {
// Cancel the coroutine started above, this doesn't affect the scope
// this coroutine was launched in
job.cancel()
}
}
}
CoroutineContext
A CoroutineContext defines the behavior of a coroutine using the following set of elements:
- Job: Controls the lifecycle of the coroutine.
- CoroutineDispatcher: Dispatches work to the appropriate thread.
- CoroutineName: The name of the coroutine, useful for debugging.
- CoroutineExceptionHandler: Handles uncaught exceptions.
Parent CoroutineContext explained
The resulting parent CoroutineContext of a coroutine can be different from the CoroutineContext of the parent since it’s calculated based on this formula:
Parent context = Defaults + inherited CoroutineContext + arguments
- Some elements have default values:
Dispatchers.Defaultis the default of CoroutineDispatcher and“coroutine”the default of CoroutineName. - The inherited CoroutineContext is the CoroutineContext of the CoroutineScope or coroutine that created it.
- Arguments passed in the coroutine builder will take precedence over those elements in the inherited context.

New CoroutineContext
New coroutine context = parent CoroutineContext + Job()
val job = scope.launch(Dispatchers.IO) {// new coroutine}

Job
A job is a handle to coroutine. If a job is not passed into CoroutineContext, Job() will be used and it will cancel all coroutines in the scope for uncaught exceptions.
val job = scope.launch {
// New coroutine
}
Job Lifecycle
A Job can go through a set of states: New, Active, Completing, Completed, Cancelling and Cancelled.

CoroutineDispatcher
Kotlin coroutines use dispatchers to determine which threads are used for coroutine execution. Coroutines can suspend themselves, and the dispatcher is responsible for resuming them.
withContext is usually called to switch dispatcher.
Customize Dispatcher
- Private thread pools can be created with newSingleThreadContext and newFixedThreadPoolContext.
- An arbitrary java.util.concurrent.Executor can be converted to a dispatcher with the asCoroutineDispatcher extension function.
CoroutineName
The name of the coroutine, useful for debugging.
CoroutineExceptionHandler
An optional element in the coroutine context to handle uncaught exceptions.
- Normally, uncaught exceptions can only result from root coroutines as all children coroutines delegate handling of their exceptions to their parent.
- Coroutines running with
SupervisorJobdo not propagate exceptions to their parent and treated like root coroutines. - A coroutine that was created using async always catches all its exceptions and represents them in the resulting Deferred object, so it cannot result in uncaught exceptions.
Uncaught exceptions with no handler
- If exception is CancellationException, it is ignored, as these exceptions are used to cancel coroutines.
- Otherwise, if there is a Job in the context, then Job.cancel is invoked.
- Otherwise, as a last resort, the exception is processed in a platform-specific manner:
- On JVM, all instances of CoroutineExceptionHandler found via ServiceLoader, as well as the current thread’s Thread.uncaughtExceptionHandler, are invoked.
References
Coroutines: first things first Developer Android Kotlin Coroutine