异常处理的必要性
- 当应用出现一些意外情况时,给用户提供合适的体验非常重要,一方面,目睹应用崩溃是一个很糟糕的体验, 另一方面,在用户操作失败时,也必须要能给出正确的提示信息。
异常的传播
- 协程构建器有两种形式:自动传播异常(launch与actor),向用户暴露异常(async与produce)当这些构建器用于创建一个根协程时(该协程不是另一个协程的子协程), 前者这类构建器,异常会在它发生的第一时间被抛出,而后者则依赖用户来最终消费异常,例如通过await或receive。
@Test
fun `test exception propagation`() = runBlocking<Unit> {
val job = GlobalScope.launch {
try {
throw IndexOutOfBoundsException()
} catch (e: Exception) {
println("Caught IndexOutOfBoundsException")
}
}
job.join()
val deferred = GlobalScope.async {
throw ArithmeticException()
}
try {
deferred.await()
} catch (e: Exception) {
println("Caught ArithmeticException")
}
}
非根协程异常
@Test
fun `test exception propagation2`() = runBlocking<Unit> {
val scope = CoroutineScope(Job())
val job = scope.launch {
async {
throw IllegalArgumentException()
}
}
job.join()
}
异常的传播特性
- 当一个协程由一个异常而运行失败时,它会传播这个异常并传递给它的父级。接下来,父级会进行下面几步操作:
- 1.取消它自己的子级
- 2.取消它自己
- 3.将异常传播并传递给它的父级

SupervisorJob
- 使用SupervisorJob时,一个子协程的运行失败不会影响到其他子协程。SupervisorJob不会传播异常给它的父级,它会让子协程自己处理异常。
- 这种需求常见于在作用域内定义作业的UI组件,如果任何一个UI的子作业执行失败了,它并不总是有必要取消整个UI组件, 但是如果UI组件被销毁了,由于它的结果不再被需要了,它就有必要使所有的子作业执行失败。
@Test
fun `test SupervisorJob`() = runBlocking<Unit>