This series of blog posts goes in-depth into cancellation and exceptions in Coroutines. Cancellation is important for avoiding doing more work than needed which can waste memory and battery life; proper exception handling is key to a great user experience. As the foundation for the other 2 parts of the series (part 2: cancellation, part 3: exceptions), it’s important to define some core coroutine concepts such as CoroutineScope, Job and CoroutineContext so that we all are on the same page.
If you prefer video, check out this talk from KotlinConf’19 by
and I:
CoroutineScope
A CoroutineScope keeps track of any coroutine you create using launch or async (these are extension functions on CoroutineScope). The ongoing work (running coroutines) can be canceled by calling scope.cancel() at any point in time.
You should create a CoroutineScope whenever you want to start and control the lifecycle of coroutines in a particular layer of your app. In some platforms like Android, there are KTX libraries that already provide a CoroutineScope in certain lifecycle classes such as viewModelScope and lifecycleScope.
When creating a CoroutineScope it takes a CoroutineContext as a parameter to its constructor. You can create a new scope & coroutine with the following code:
// Job and Dispatcher are combined into a CoroutineContext which
// will be discussed shortly
val scope = CoroutineScope(Job() + Dispatchers.Main)val job = scope.launch {
// new coroutine
}
Job
A Job is a handle to a coroutine. For every coroutine that you create (by launch or async), it returns a Job instance that uniquely identifies the coroutine and manages its lifecycle. As we saw above, you can also pass a Job to a CoroutineScope to keep a handle on its lifecycle.