Job cancellation

Internally, the execute method calls the launch coroutine builder to fire off a coroutine Job. The scope for these coroutines will be a CoroutineScope contained by the ViewModel.

abstract class RainbowCakeViewModel<VS : Any>(initialState: VS) : ViewModel() {

    private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
    // ...

The context of the scope is made up of two components: the Main dispatcher and a SupervisorJob instance that acts as the parent of all coroutines launched in the ViewModel.

  • Dispatchers.Main describes the CoroutineDispatcher element to use, in this case, the Android UI thread. This will be used until the job is placed on another dispatcher (for example, with a withContext call in the Presenter).
  • SupervisorJob() provides the parent Job element for the coroutine. This is a simple, empty Job, only used to group the jobs launched inside a given ViewModel. This job is cancelled (indirectly, by cancelling the scope itself) when the ViewModel’s onCleared method is called. This happens when the lifecycle it was attached to has terminated, meaning the Activity/Fragment is actually destroyed for good, and not just going through configuration changes.
override fun onCleared() {
    log(logTag, "ViewModel cleared, job cancelled")

Cancelling the root job will also cancel all of its child coroutines, which in this case is every coroutine launched with the execute method. Note that coroutine cancellation is cooperative. Blocking code running inside the coroutine will not be cancelled. Cancellation can only happen in suspending functions, and only if it’s handled explicitly (or by functions from kotlinx.coroutines that support it). Retrofit’s coroutine integration is a good example of this.


The reasoning for using a SupervisorJob (as well as other good coroutine tips) is laid out nicely in this article.

The job cancellation approach is also based on the “How to cancel a coroutine” section of this blog post.