2017-07-26 187 views
3

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

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()抛出CancellationException其被成功地捕获。但随后的RuntimeException未被捕获。我认为作业被取消后的以下异常应该被lib忽略?或者我可以如何在不抛出CancellationException异常的情况下默默取消工作?

+0

你可以把'试穿catch'左右'推出(){}' ? – voddan

+0

@voddan nope,'launch'立即执行。但我找到了解决方案 –

回答

4

使用CoroutineExceptionHandler作为additinoal协程上下文来处理异常,为launchrun

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

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

感谢您的信息。我们是否应该为我们的代码中的每个“启动”或“运行”用法这样做?在生产代码中,我们通常希望异常情况冒泡,以便Crashlytics或其他记录器可以捕获它们。有没有更多...混乱...方式做到这一点? – Pablo

+0

我想问我们是否可以替换“启动”功能。异步函数以相同的方式捕获异常?我已经尝试过并让它失败。 至少我可以使用“withContext”函数来使用你的catch异常方法 –