Android ViewModel 中令人困惑的协程行为

问题描述 投票:0回答:1

我正在尝试在ViewModel中探索和实现协程取消/异常恢复机制。我发现我的 ViewModel 中的以下代码没有捕获异常并使应用程序崩溃:

viewModelScope.launch {
    try {
        supervisorScope {
            launch {
                throw Exception()
            }
        }
    } catch (e: Exception) {
        println("Exception caught")
    }
}

但是如果我用

supervisorScope
替换
coroutineScope
它就会被抓住。两种情况都不应该被抓住吗?谁能解释一下为什么supervisorScope作用域异常在这里取消了它的父作用域?

kotlin kotlin-coroutines android-viewmodel structured-concurrency
1个回答
0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.