我将翻译三篇介绍协程的 取消 和 异常处理 相关的文章,三篇文章是层层递进的关系。翻译过程中我将尽量忠实于原文。当然,由于水平有限,不能保证完全的翻译正确。如果您发现翻译有错误之处,欢迎在评论里指出。我也将贴出每篇翻译的原文。
- 第一篇:《协程:第一件事》(原文: Coroutines: first things first)
- 第二篇:《协程的取消》(原文:Cancellation in coroutines)
- 第三篇:《协程的异常》(原文:Exceptions in coroutines)
这是第三篇。
文章目录
我们开发者通常花费大量时间打磨应用的正常使用场景。然而,当应用发生意外时,合适的用户体验也同样重要。一方面,对用户来说应用发生崩溃是糟糕的体验;另一方面,当操作执行失败时,给用户提示适当的信息是必不可少的。
合理的异常处理将对用户如何看待你的应用产生很大的影响。在这篇文章中,我们将解释协程中的异常如何传播,以及如何通过多种方式控制它们。
协程突然失败了!怎么办?😱
当一个协程因为异常失败时,它会把异常传播给它的父节点!然后,这个父节点会 1) 取消其余的子协程,2) 取消自己, 3) 把异常传播给自己的父节点。
异常将被传播到层次结构的根节点,所有由这个根节点 CoroutineScope
发起的协程都将被取消。
(协程中发生的异常将通过协程的层次结构向上传播)
有些情况下这种异常传播是合理的,然而也有些情况不需要这样。试想,有一个与UI相关的 CoroutineScope
,用来处理用户交互。如果其中一个子协程抛了异常,那么这个UI scope
将被取消,并且整个UI模块将变得无法响应,因为一个被取消的 scope
不能再启动协程。
如果你不想要这样的行为该怎么办?作为替代,你可以在创建协程的 CoroutineScope
的 CoroutineContext
中用一个不同的 Job
实现,名叫 SuervisorJob
。
拯救者 SupervisorJob
使用 SupervisorJob
,子协程的失败不会影响其余子协程。一个 SupervisorJob
不会取消它自己或它的子协程。而且,SupervisorJob
也不会传播异常,它会让子协程自己处理异常。
你可以这样创建一个 CoroutineScope
:
val uiScope = CoroutineScope(SupervisorJob())
当一个协程失败时,它不会传播取消,如下图所示:
(一个 SupervisorJob
不会因为异常取消自身或其余的子协程)
如果异常没有被处理,并且 CoroutineContext
没有 CoroutineExceptionHandler
,异常将到达默认线程的 ExceptionHandler
。在JVM中,异常将被记录到控制台;在Android中,不管异常发生在哪个线程,都将导致app崩溃。
💥未捕获的异常总会被抛出,不管你使用哪种
Job
同样的行为也适用于 scope
构建器 coroutineScope
和 supervisorScope
。这两种构建器将创建一个子 scope
(使用 Job
或 SupervisorJob
),你可以用它们来组织协程(比如说你想做并行计算,或者你希望它们相互影响or相互不影响)。
提醒:SupervisorJob
只有在其作为 scope
的一部分时才起作用,要么使用 supervisorScope
创建,要么使用 CoroutineScope(SuperVisorJob())
创建。
Job 还是 SupervisorJob ?🤔
什么时候该使用 Job
或者 SupervisorJob
?当你不希望失败导致父节点或同级节点被取消时&#