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

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

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

现在,在Kotlin中,我们可以从Activity中的lifecycleScope或如果使用ViewModel的viewModelScope中启动以下等效代码:
viewModelScope.launch {
    try {
        someBgOperation()
    } catch (e: Exception){
        //show dialog
    }
}

Activity 关闭时,这两个作用域都会自动取消,这与Autodispose一样。但是catch块不仅会通过someBgOperation本身抛出的正常错误执行,还会通过协程库在幕后使用的CancellationException来执行取消操作。如果在 Activity 关闭时尝试在其中显示对话框,则可能会出现新的异常。所以我被迫做这样的事情:
viewModelScope.launch {
    try {
        someBgOperation()
    } catch (ce: CancellationException){
        //do nothing, activity is closing
    } catch (e: Exception){
        //show dialog
    }
}

这比Rx版本更冗长,并且具有空的catch子句,该子句会在lint输出中显示警告。在其他情况下,在 try catch 后执行更多操作时,我被迫从CancellationException捕获返回以保持UI安全(这些返回被标记为返回)。我发现自己一次又一次地重复这个丑陋的模板。

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

最佳答案

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

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

另一方面,您可以使用Kotlin Flow,其Kotlin Flow的catch运算符旨在完全忽略此取消。由于实际上您将不会在该流上发送任何值,因此您应该使用Flow<Nothing>:
flow<Nothing> {
    someBgOperation()
}.catch { e ->
    // show dialog
}.launchIn(viewModelScope)

关于android - Kotlin:如何绕过CancellationException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62220286/

10-11 07:13