Kotlin:如何绕过 CancellationException

2024-03-20

我正在将一些旧的 RxJava 代码移植到协程中。使用 RxJava,我可以在我的活动中执行此操作:

someBgOperation()
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(MyActivity.this)))
.subscribe(
    MyActivity.this::onSuccess,
    MyActivity.this::onError
);

如果活动正在关闭,自动处置库将取消 Observable。在这种情况下,RxJava 不会调用错误处理程序,因此可以在错误处理程序中安全地执行与 UI 相关的操作,例如显示对话框。

现在,在 Kotlin 中,我们可以从以下位置启动等效代码lifecycleScope在活动中,或在viewModelScope如果使用 ViewModel:

viewModelScope.launch {
    try {
        someBgOperation()
    } catch (e: Exception){
        //show dialog
    }
}

当活动关闭时,两个作用域都会自动取消,就像 Autodispose 所做的那样。但是 catch 块不仅会在抛出正常错误时执行someBgOperation本身,但也与CancellationException协程库在幕后使用它来处理取消。如果我尝试在活动关闭时显示一个对话框,我可能会遇到新的异常。所以我被迫做这样的事情:

viewModelScope.launch {
    try {
        someBgOperation()
    } catch (ce: CancellationException){
        //do nothing, activity is closing
    } catch (e: Exception){
        //show dialog
    }
}

这感觉比 Rx 版本更详细,并且有一个空的 catch 子句,这会在 lint 输出中显示警告。在其他情况下,如果我在 try-catch 之后执行更多操作,我将被迫从CancellationExceptioncatch 以保持 UI 安全(并且这些返回值被标记为返回值)。我发现自己一次又一次地重复这个丑陋的模板。

有没有更好的方法来忽略 CancellationException?


我可以提出两种解决方案。首先,额外的catch(e: CancellationException)子句看起来有点冗长。您可以将代码简化为:

viewModelScope.launch {
    try {
        someBgOperation()
    } catch (e: Exception) {
        if (e !is CancellationException) // show dialog
    }
}

另一方面,您可以使用 Kotlin Flow,其catch运算符正是为此目的而设计忽略取消的。由于您实际上不会通过流发送任何值,因此您应该使用Flow<Nothing>:

flow<Nothing> {
    someBgOperation()
}.catch { e ->
    // show dialog
}.launchIn(viewModelScope)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin:如何绕过 CancellationException 的相关文章

随机推荐