Kotlin Coroutine SupervisorJob 取消行为

Kotlin Coroutine SupervisorJob canceling behaviour

代码:

viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
                Log.e("TAG", "task 1")
            }) {
                try {
                    Log.e("TAG", "task 1 start")
                    delay(3000)
                    Log.e("TAG", "task 1 finished")
                } catch (ex: Exception) {
                    Log.e("TAG", "task 1 cancelled " + ex)
                }
            }

    launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
        Log.e("TAG", "handler 1" + myJob?.isCancelled)
    }) {

        myJob = SupervisorJob()
        launch(myJob!! + CoroutineExceptionHandler { _, _ ->
            Log.e("TAG", "handler 2 " + myJob?.isCancelled)
        }) {

            delay(300)
            launch {
                try {
                    Log.e("TAG", "task 2 start")
                    delay(5000)
                    Log.e("TAG", "task 2 finished")
                } catch (ex: Exception) {
                    Log.e("TAG", "task 2 ex " + ex)
                }
            }

            launch {
                delay(2000)
                throw Exception()
            }

        }

        launch {
            try {
                Log.e("TAG", "task 3 start")
                delay(3000)
                Log.e("TAG", "task 3 finished")
            } catch (ex: Exception) {
                Log.e("TAG", "task 3 ex " + ex)
            }
        }
    }

输出:

2020-01-06 09:47:56.462 7159-7159/? E/TAG: task 1 start
2020-01-06 09:47:56.496 7159-7159/? E/TAG: task 3 start
2020-01-06 09:47:56.798 7159-7159/com.mvvm.template.debug E/TAG: task 2 start
2020-01-06 09:47:58.822 7159-7159/com.mvvm.template.debug E/TAG: task 2 ex kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=StandaloneCoroutine{Cancelling}@81a8e39
2020-01-06 09:47:58.827 7159-7159/com.mvvm.template.debug E/TAG: handler 2 false
2020-01-06 09:47:59.464 7159-7159/com.mvvm.template.debug E/TAG: task 1 finished
2020-01-06 09:47:59.499 7159-7159/com.mvvm.template.debug E/TAG: task 3 finished

我的问题:

我无法理解为什么当任务 2 是 SupervisorJob 的 child 时被取消,而异常发生在另一个 child。

文档状态:

child 的失败或取消不会导致主管作业失败,也不会影响其他 children,因此主管可以实施自定义策略来处理其作业的失败child仁。 我错过了什么吗?任何帮助将不胜感激。

您的答案就在日志中:

task 2 ex kotlinx.coroutines.JobCancellationException: Parent job is Cancelling;
job=StandaloneCoroutine{Cancelling}@81a8e39

看看 parent 工作:这是一份 StandaloneCoroutine 而不是你的 SupervisorJob

写的时候

launch(myJob!!, handler) { ... }

myJob 成为已启动协程的 parent 并且协程本身始终与 launch 函数创建的作业相关联它,类型 StandaloneCoroutine。在这个协程中,您可以启动更多协程而无需明确指定 parent,这意味着它们的 parent 是协程的工作。这不是主管职位,因此会被取消。

I’m having an issue to understand why task 2 is canceled when it’s a child of SupervisorJob and the exception happened on an other child.

这不是问题的直接答案,因为接受的答案已经很好地回答了这个问题,因为 parent 不是 SupervisorJob

但为了实现使 task2 独立于同级作业的预期行为:

您必须通过以下任一方式使用 SupervisorJob

  1. 使您希望彼此独立的每项任务的 SupervisorJob parent:

在你的例子中:

val myJob = SupervisorJob()
    val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
        Log.e("TAG", "handler 1" + myJob.isCancelled)
    }) {

        launch(CoroutineExceptionHandler { _, _ ->
            Log.e("TAG", "handler 2 " + myJob.isCancelled)
        }) {

            delay(300)
            launch(myJob) { // <<<<<<<<< myJob is the parent
                try {
                    Log.e("TAG", "task 2 start")
                    delay(5000)
                    Log.e("TAG", "task 2 finished")
                } catch (ex: Exception) {
                    Log.e("TAG", "task 2 ex $ex")
                }
            }

            launch(myJob) { // <<<<<<<<< myJob is the parent
                delay(2000)
                throw Exception()
            }

        }

        //......
    }
  1. supervisorScope suspend function
  2. 中添加 child 工作兄弟姐妹
val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
    Log.e("TAG", "handler 1")
}) {
    launch(CoroutineExceptionHandler { _, _ ->
        Log.e("TAG", "handler 2 ")
    }) {

        delay(300)

        supervisorScope { /// <<<< supervisor scope
            launch {
                try {
                    Log.e("TAG", "task 2 start")
                    delay(5000)
                    Log.e("TAG", "task 2 finished")
                } catch (ex: Exception) {
                    Log.e("TAG", "task 2 ex $ex")
                }
            }

            launch {
                delay(2000)
                throw Exception()
            }
        }

    }

    //....
}

现在 任务 2 将完成而不会引发 JobCancellationException