【问题标题】:Can't catch exception throwing after Kotlin coroutine is cancelledKotlin协程取消后无法捕获异常抛出
【发布时间】:2018-01-01 18:26:20
【问题描述】:

使用kotlinx.coroutines lib 如果在取消协程后抛出异常,我将无法捕获异常。这会导致应用程序崩溃。

fun foo() {
  val job = launch(UI) {
     try {
        Log.d("TAG", "Start coroutine")
        run(CommonPool) {
           Log.d("TAG", "Start bg task")
           // Intentionally make bg task running for a long time
           SystemClock.sleep(2000)
           Log.d("TAG", "Throw bg task exception")
           throw RuntimeException("Bg task exception")
        }
     } catch (e: Exception) {
        Log.e("TAG", "Handle coroutine exception", e)
     }
  }

  launch(UI) {
     delay(1000)
     Log.d("TAG", "Cancel job = ${job.cancel()}")
  }

}

在 Android 上运行此函数会产生以下日志输出

07-26 15:09:10.038 31518-31518/co.foo.bar D/MainActivity: Start coroutine
07-26 15:09:10.044 31518-31547/co.foo.bar D/MainActivity: Start bg task
07-26 15:09:11.046 31518-31518/co.foo.bar D/MainActivity: Cancel job = true
07-26 15:09:11.047 31518-31518/co.foo.bar E/MainActivity: Handled coroutine exception
                           java.util.concurrent.CancellationException: Job was cancelled
                           at kotlinx.coroutines.experimental.JobSupport$CompletedExceptionally.getException(Job.kt:921)
                           at kotlinx.coroutines.experimental.RunCompletion.afterCompletion(Builders.kt:198)
                           ...
                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
07-26 15:09:12.046 31518-31547/co.foo.bar D/MainActivity: Throwing bg task exception

--------- beginning of crash
07-26 15:09:12.046 31518-31547/co.foo.bar E/AndroidRuntime: FATAL EXCEPTION: ForkJoinPool.commonPool-worker-1
Process: co.foo.bar, PID: 31518
                           java.lang.RuntimeException: Bg task exception
                           at co.foo.barsample.MainActivity$onCreate$1$job$1$1.doResume(MainActivity.kt:36)
                           at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
                           at kotlinx.coroutines.experimental.DispatchTask.run(CoroutineDispatcher.kt:120)
                           at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1383)
                           at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:256)
                           at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1123)
                           at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1961)
                           at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1909)
                           at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:128)
07-26 15:09:12.050 1705-2190/system_process W/ActivityManager:   Force finishing activity co.foo.bar/co.foo.barsample.MainActivity

似乎正在调用cancel() throws CancellationException 被成功捕获。但是后面的RuntimeException 没有被抓到。我想lib应该忽略作业取消后的以下异常吗?或者如何在不引发CancellationException 异常的情况下静默取消作业?

【问题讨论】:

  • 你能把try-catch 放在launch(){} 周围吗?
  • @voddan nope,launch 立即执行。但我找到了解决方案

标签: android kotlin coroutine kotlinx.coroutines


【解决方案1】:

使用CoroutineExceptionHandler 作为额外的协程上下文来处理异常,例如launchrun

run(CommonPool + CoroutineExceptionHandler({ _, e ->
   Log.e("TAG", "CoroutineExceptionHandler", e)
})) {
    ...
}

launch(UI + CoroutineExceptionHandler({ _, e ->
   Log.e("TAG", "CoroutineExceptionHandler", e)
})) {
   ...
}

【讨论】:

  • 感谢您的信息。我们是否应该对代码中的每个launchrun 使用都这样做?在生产代码中,我们通常希望异常冒泡,以便 Crashlytics 或其他记录器可以捕获它们。有没有一种不那么……杂乱……的方法来做到这一点?
  • 我想问一下我们是否可以替换“启动”功能。到异步函数以相同的方式捕获异常?我已经尝试过了,让它失败了。至少我可以使用“withContext”函数来使用你的捕获异常方法
猜你喜欢
  • 2021-09-02
  • 2011-04-05
  • 2015-09-23
  • 2013-08-02
  • 2018-05-30
  • 2019-04-13
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
相关资源
最近更新 更多