Mis*_*ith 6 android cancellation kotlin kotlin-coroutines
我正在将一些旧的 RxJava 代码移植到 Coroutines。使用 RxJava,我可以在我的活动中做到这一点:
someBgOperation()
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(MyActivity.this)))
.subscribe(
MyActivity.this::onSuccess,
MyActivity.this::onError
);
Run Code Online (Sandbox Code Playgroud)
如果活动正在关闭,自动处理库将取消 Observable。在这种情况下,RxJava 不会调用错误处理程序,因此可以在错误处理程序中安全地执行 UI 相关的操作,例如显示对话框。
现在,在 Kotlin 中,我们可以lifecycleScope在 Activity 中或在viewModelScope使用 ViewModel 的情况下启动此等效代码:
viewModelScope.launch {
try {
someBgOperation()
} catch (e: Exception){
//show dialog
}
}
Run Code Online (Sandbox Code Playgroud)
当活动关闭时,两个作用域都会自动取消,就像 Autodispose 所做的那样。但是 catch 块不仅会执行someBgOperation它自己抛出的正常错误,还会执行CancellationException引擎盖下的协程库用来处理取消的 s。如果我在活动关闭时尝试在那里显示一个对话框,我可能会收到新的异常。所以我被迫做这样的事情:
viewModelScope.launch {
try {
someBgOperation()
} catch (ce: CancellationException){
//do nothing, activity is closing
} catch (e: Exception){
//show dialog
}
}
Run Code Online (Sandbox Code Playgroud)
这感觉比 Rx 版本更冗长,并且它有一个空的 catch 子句,它会在 lint 输出中显示警告。在 try-catch 之后我做更多事情的其他情况下,我被迫从CancellationExceptioncatch返回以保持 UI 安全(这些返回是标记返回)。我发现自己一次又一次地重复这个丑陋的模板。
有没有更好的方法来忽略 CancellationException?
我可以提出两种解决方案。首先,附加catch(e: CancellationException)条款看起来有点冗长。您可以将代码简化为:
viewModelScope.launch {
try {
someBgOperation()
} catch (e: Exception) {
if (e !is CancellationException) // show dialog
}
}
Run Code Online (Sandbox Code Playgroud)
另一方面,您可以使用 Kotlin Flow,其catch运算符旨在为此而忽略取消。由于您实际上不会通过流程发送任何值,因此您应该使用Flow<Nothing>:
flow<Nothing> {
someBgOperation()
}.catch { e ->
// show dialog
}.launchIn(viewModelScope)
Run Code Online (Sandbox Code Playgroud)
编辑:修改,因为 CancellationExceptions 不应该被吞掉。
您可以创建一个转换为 a 的辅助函数Result,以便您只能处理非取消异常:
public inline fun <T, R> T.runCatchingCancellable(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
if (e is CancellationException) {
throw e
}
Result.failure(e)
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
viewModelScope.launch {
runCatchingCancellable {
someBgOperation()
}.onFailure { e ->
//show dialog
}
}
Run Code Online (Sandbox Code Playgroud)
此函数可以作为runCatching在可取消协程中使用的安全替代方案。
我会考虑这个稍微干净的语法:
viewModelScope.launch {
try {
someBgOperation()
} catch (e: Exception){
if (isActive) {
//show dialog
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
931 次 |
| 最近记录: |