Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

📚 If you are a beginner to Coroutines on the Android platform, check out the previous article: The Android Coroutines Series: Getting Started

Job

val job = lifecycleScope.launch { }
Copy the code

Job is used to process coroutines. For each coroutine you create (via launch or Async), it returns a Job instance that uniquely identifies the coroutine and is responsible for managing its life cycle. As we saw above, you can pass the Job instance to the CoroutineScope to control its life cycle.

A Job can be associated with multiple sub-jobs, and it also provides the implementation of passing the parent externally

public fun Job(parent: Job? = null): Job = JobImpl(parent)
Copy the code

When passed to a parent, the Job will be a child Job of the parent.

Job life cycle

Since a Job manages coroutines, it provides six states to represent the running state of the coroutine.

  1. New: create
  2. ActiveRun:
  3. Completing: The subcoroutine has finished waiting for itself
  4. CompletedCompleted:
  5. Cancelling: Canceling or failing
  6. Cancelled: Cancels or fails

The six states of jobs expose three states, which can be obtained through jobs at any time

public val isActive: Boolean
public val isCompleted: Boolean
public val isCancelled: Boolean
Copy the code

If the coroutine isActive, an error running coroutine or a call to job.cancel() will set the current task to Cancelling (isActive = false, isCancelled = true). When all subcoroutines have completed, the coroutine enters the Cancelled state, isCompleted = true.

val scope = CoroutineScope(Job())

scope.launch() {
    // new coroutine -> can suspend
    launch {
        // Child 1
    }
    launch {
        // Child 2}}Copy the code

As mentioned above, a Job can have multiple child jobs. Therefore, the completion of a Job must wait for all its child jobs to complete. The corresponding cancel is the same.

By default, if an internal child Job is abnormal, its parent Job and its related child jobs are cancelled. Commonly known as a chain reaction.

We can also change the default, and Kotlin provides the SupervisorJob to change it. It’s also common to see a situation where you’re using a coroutine to request two interfaces, but you don’t want one interface to fail and the other interface won’t be able to request it, so it’s useful to SupervisorJob to change the default mechanism for the coroutine.

SupervisorJob

When it is used in the container, the failure of one child coroutine will not affect other children. The container is designed to handle exceptions on its own instead of canceling it and its children or propagating exceptions to its parent. It is designed to let the child coroutine handle exceptions on its own.

It’s only designed to be used in the following two ways.

  • supervisorScope{}
  • CoroutineScope(SupervisorJob())

It’s a big mistake to think that when you’re running the Job it’s going to be able to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, it’s going to run, is it going to run on other coroutines

SupervisorJob use

fun main(a) {
    val scope = CoroutineScope(SupervisorJob())

    scope.launch {
        // Child 1
        println("Child 1")
        throw error("Child 1 error ")
    }
    scope.launch {
        // Child 2
        println("Child 2 before")
        delay(1000)
        println("Child 2")}Sleep only keeps the process alive in order to wait for the coroutine to complete
    Thread.sleep(3000)}Copy the code

If CoroutineScope(Job()) is used,Child 1 will throw an exception and Child 2 will be cancelled (cancellation is conditional,🔺 note: While all suspension functions in the kotlinx.coroutines package are undoable, Child 2 will continue to run when Child 1 throws an exception instead of the CoroutineScope(container).

SupervisorScope {} to use

suspend fun main(a) {

    supervisorScope {
        launch {
            // Child 1
            println("Child 1")
            throw error("Child 1 error ")
        }
        launch {
            // Child 2
            println("Child 2 before")
            delay(1000)
            println("Child 2")}}Sleep only keeps the process alive in order to wait for the coroutine to complete
    Thread.sleep(3000)}Copy the code

So this is going to be the same thing

❌ Note: incorrect spelling

Methods a ❌

val scope = CoroutineScope(Job())

scope.launch(SupervisorJob()) {
    // new coroutine -> can suspend
    launch {
        // Child 1
    }
    launch {
        // Child 2}}Copy the code

Methods two ❌

val scope = CoroutineScope(SupervisorJob())

scope.launch() {
    // new coroutine -> can suspend
    launch {
        // Child 1
    }
    launch {
        // Child 2}}Copy the code

The two notations above are not designed to be seen as container for handling

Because when a new coroutine is created, a new Job instance is created