1. Introduction

If I had to make an analogy between a real thing and a Job in Kotlin’s coroutine, I would compare a Job to an iceberg in the ocean. About 90% of a free-floating iceberg lies below the surface of the water, so it is impossible to guess the shape of an iceberg below the surface. Like the iceberg, Job provides very simple functionality to developers, but Job does a lot of work inside the coroutine framework. Crucially, if a developer does not understand the implementation mechanics inside a Job, he is like the captain of a ship that has missed an iceberg and is at risk of a system crash. If you are using coroutines in your projects, if you want to enjoy the convenience of coroutine programming while keeping your programs robust. Understanding the inner workings of a Job will give you a lot of comfort when it comes to coroutine problems.

Which Case “CancelJobActivity job2 FINISHED” statement will be printed?

A program:

private fun case1() {
    val scope = MainScope()
    scope.launch(Job()) {
        launch {
            delay(2000L)
            println("CancelJobActivity job1 finished")
            scope.cancel()
        }
        launch {
            delay(3000L)
            println("CancelJobActivity job2 finished")
        }
    }
}
Copy the code

Program 2:

private fun case2() {
    val scope = MainScope()
    scope.launch {
        launch {
            delay(2000L)
            println("CancelJobActivity job1 finished")
            scope.cancel()

        }
        launch {
            delay(3000L)
            println("CancelJobActivity job2 finished")
        }
    }
}
Copy the code

The function of the above two pieces of code is to start a coroutine through MainScope and to start two child coroutines within the coroutine. Subcoroutine 1 prints statements after delay 2s and cances the coroutine started by MainScope; subcoroutine 2 prints statements after delay 3s. Our expectation is that after subcoroutine 1 calls the scope.Cancel method, subcoroutine 2 will not print a statement. Program two did what we wanted, but program one still printed out after 3s. Obviously the result of procedure one is not acceptable to us. The only difference is whether there is a Job() parameter at scope.launch.

Why can’t you cancel the coroutine if the launch() method increases the Job() parameter? And to understand that, you have to understand the data structure in the parent coroutine. This is a typical tree data structure, as we can see from Google’s official public images.

2. Cause analysis

  1. According to the MainScope source code, we know that the Job type of scope is container Job.
  2. The coroutine created by the launch method is itself of type Job. And it forms a parent-child relationship with the CoroutineContext[Job] that started the coroutine.
  3. The launch(Job() is designed for CoroutineContext’s plus method, and the new newJob is being replaced with the SupervisorJob.
public fun MainScope(): CoroutineScope 
= ContextScope(SupervisorJob() + Dispatchers.Main)
Copy the code
  1. The coroutinescope. cancel method is the Job cancel corresponding to coroutineContext[Job].
public fun CoroutineScope.cancel(cause: CancellationException? = null) { val job = coroutineContext[Job] ? : error("Scope cannot be cancelled because it does not have a job: $this") job.cancel(cause) }Copy the code

Diagram of procedure two

Job diagram for program one

It can be seen from the Job relation diagram that the MainScope’s corresponding SupervisorJob is independently dissociated in program 1 and does not form an effective tree structure relation with Job0, Job1 and Job2, so it cannot be cancelled through scope.cancel().

In the end, I have been thinking about the article related to Job for a long time. Due to the complexity of Job, I didn’t know how to start it. This article as a start, Job principle and source code combined with the article, will be updated in succession. The cases in this paper are only the tip of the iceberg in Job problems. Cancel and exception handling mechanisms can be used in many ways in relation to the Job tree structure diagram. However, it is not difficult to deal with the problem if you truly understand the principle. Next time, I will explain how the tree structure of the Job is established, and how the cancel mechanism of the Job propagates in both directions and the exception handling mechanism propagates upwards.

If you have any questions, please join my technical communication group for discussion. Background reply "communication" can be