我正在尝试在ViewModel中探索和实现协程取消/异常恢复机制。我发现我的 ViewModel 中的以下代码没有捕获异常并使应用程序崩溃:
viewModelScope.launch {
try {
supervisorScope {
launch {
throw Exception()
}
}
} catch (e: Exception) {
println("Exception caught")
}
}
但是如果我用
supervisorScope
替换 coroutineScope
它就会被抓住。两种情况都不应该被抓住吗?谁能解释一下为什么supervisorScope作用域异常在这里取消了它的父作用域?
与
supervisorScope
相比,coroutineScope
的主要功能是保持运行,即使子协程失败也是如此。如果有多个孩子在跑步且不应该互相影响,这一点尤其有用。
当子协程失败时(您的示例中就是这种情况),它不会失败,而是将异常传播到其自己的父协程。在你的例子中,这是在
viewModelScope
中启动的协程。这有效地绕过了您的 try/catch 块并直接导致 viewModelScope
失败。作为旁注,如果您直接在 supervisorScope
而不是嵌套的 launch
中抛出异常,则 supervisorScope
would 会失败并且异常 would 会被 try/catch 块捕获。只有失败的孩子才会被supervisorScope
特别处理。
作为 try/catch 块的替代方案,协程中的异常可以由 CoroutineExceptionHandler 有效处理:
val handler = CoroutineExceptionHandler { _, e ->
println("Exception caught: $e")
}
viewModelScope.launch(handler) {
supervisorScope {
launch {
throw Exception()
}
}
}
现在,当
supervisorScope
将异常传播到父级 launch
时,在那里定义的异常处理程序会捕获异常并防止协程被取消,因此也可以防止您的应用程序崩溃。当使用 coroutineScope
而不是 supervisorScope
时,这也将起到相同的作用。
在文档中了解有关协程异常处理的更多信息:https://kotlinlang.org/docs/exception-handling.html