关于android:协程CancellationException预期行为

Coroutines CancellationException expected behaviour

因此基于Kotlin对协程的介绍,在取消和超时->运行不可取消的块中,我找到以下解释:Any attempt to use a suspending function in the finally block [...] causes CancellationException,但在运行时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun runPlayground() = runBlocking {
    val job = launch {
        try {
            repeat(1000) { i ->
                Log.d("XXX","job: I'm sleeping $i ...")
                delay(500L)
            }
        } finally {
            doWorld() // logs"World" after 1s delay
            delay(2000L)
            Log.d("XXX","job: I'm running finally")
        }
    }
    delay(1300L) // delay a bit
    Log.d("XXX","main: I'm tired of waiting!")
    job.cancelAndJoin() // cancels the job and waits for its completion
    Log.d("XXX","main: Now I can quit.")
}

记录结果:

1
2
3
4
5
D/XXX: job: I'm sleeping 0 ...
D/XXX: job: I'm sleeping 1 ...
D/XXX: job: I'm sleeping 2 ...
D/XXX: main: I'm tired of waiting!
D/XXX: main: Now I can quit.

最后一个块可能不执行,可能是由于在其中运行了暂停功能,但是我希望在运行时从以下位置运行该代码:

1
2
3
4
5
try {
    runPlayground()
} catch (e: CancellationException)  {
    Log.d("XXX", e.message)
}

协程内部是否处理了异常?


doWorld()抛出异常,从那时起,它逃脱了协程块并被静默地吞下,因为您尚未安装任何未处理的异常处理程序。由于该异常,正在运行的调度程序不会崩溃。

如果您在上面的链接下研究文档,您将了解到,由于该异常是CancelationException,因此它被默默地吞下了。其他任何异常至少都将以与未处理的异常出现在Java线程中相同的方式显示,例如:

1
2
3
4
5
6
fun main() = runBlocking {
    launch(Job()) {
        throw Exception("I failed")
    }.join()
    println("runBlocking done")
}

此打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Exception in thread"main" java.lang.Exception: I failed
    at org.mtopol.TestingKt$main$1$1.invokeSuspend(testing.kt:8)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at org.mtopol.TestingKt.main(testing.kt:6)
    at org.mtopol.TestingKt.main(testing.kt)

runBlocking done

尤其要注意main线程实际上并没有死:它继续打印行runBlocking done。 Kotlin只是重复使用已安装的currentThread().uncaughtExceptionHandler()来记录协程失败。

当我去测试与上面类似的代码,但是安装了CoroutineExceptionHandler时,我发现了一些问题:

  • 子协程中的异常处理程序将被忽略。这似乎是设计使然,但未记录。
  • runBlocking似乎有一个错误,即使您为其安装了处理程序,它也不会运行。
  • 我为此创建了一个问题。