+-

我正在将一些旧的RxJava代码移植到协程。使用RxJava,我可以在自己的活动中执行此操作:
someBgOperation()
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(MyActivity.this)))
.subscribe(
MyActivity.this::onSuccess,
MyActivity.this::onError
);
如果活动被关闭,自动处置库将取消Observable。在这种情况下,RxJava不会调用错误处理程序,因此可以安全地在错误处理程序中执行与UI相关的操作,例如显示对话框。
现在,在Kotlin中,我们可以从Activity中的lifecycleScope启动此等效代码,如果使用ViewModel,则可以在viewModelScope中启动此等效代码:
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输出中显示警告。在其他情况下,在尝试捕获之后执行更多操作时,我被迫从CancellationException捕获返回以保持UI安全(这些返回被标记为返回)。我发现自己一次又一次地重复这个丑陋的模板。
是否有更好的方法来忽略CancellationException?
1
投票
投票
我会考虑使用这种更简洁的语法:
viewModelScope.launch {
try {
someBgOperation()
} catch (e: Exception){
if (isActive) {
//show dialog
}
}
}
0
投票
投票
将其作为一个单独的答案发布,因为它完全不同,甚至可能有些杜鹃。
您可以创建一个函数和助手类来吞咽CancellationExceptions,同时保持类似的语法:
viewModelScope.launch {
tryCancellable {
someBgOperation()
} catchNotCancelled { e ->
//show dialog
}
}
/** Run the block of code and catch any throwable besides CancellationException into
the returned [AttemptResult]. */
suspend fun tryCancellable(block: suspend () -> Unit ): AttemptResult {
var throwable: Throwable? = null
try {
block()
} catch (e: CancellationException) {
} catch (e: Throwable) {
throwable = e
}
return AttemptResult(throwable)
}
class AttemptResult(val throwable: Throwable?) {
/** Handle the wrapped throwable if it exists. */
infix fun catchNotCancelled(block: (Throwable) -> Unit) {
if (throwable != null)
block(throwable)
}
fun rethrow() = throwable?.let { throw it }
}