Kotlin协程中CancellationException在CoroutineScope内不崩溃的原因
Kotlin协程CancellationException特殊行为解答
1. 协程内抛出CancellationException不触发崩溃的核心原因
这是Kotlin协程框架对取消逻辑的专门设计,核心逻辑有三点:
CancellationException在协程体系里不是代表执行错误的异常,而是协程正常取消的流程控制信号。协程采用协作式取消机制,当调用cancel/cancelChildren方法时,会给对应Job设置已取消标记,后续协程执行到挂起点时就会抛出这个异常,用来中断当前执行流程,逐层退出代码块、清理资源,完成整个取消流程。- 协程框架在Job的状态流转逻辑中,会专门识别并内部捕获所有抛出的
CancellationException,不会将其归类为「执行失败异常」传递给CoroutineExceptionHandler,自然也不会走到Android系统的全局未捕获异常逻辑,因此不会触发崩溃,框架本身也不会打印错误日志。 - 这套特殊处理逻辑仅在协程上下文内生效:如果在协程上下文之外直接抛出
CancellationException,没有框架层的专门捕获,它就和普通异常没有区别,会直接走到线程未捕获异常处理器,触发应用崩溃。
对比测试中抛出IllegalStateException的场景:这类非取消类异常会被协程判定为执行失败,向上逐层传播给父协程,最终交给CoroutineExceptionHandler处理,Android平台默认的处理逻辑就是将异常抛给系统,触发应用崩溃。
2. 自定义抛出后不触发崩溃的异常的实现方式
分两种场景处理:
协程范围内使用的取消类异常
直接让自定义异常继承CancellationException即可,协程框架对所有CancellationException的子类都采用相同的处理逻辑,会将其识别为正常取消信号,内部捕获消化,不会触发崩溃:
// 自定义协程取消异常 class CustomCoopCancelException( message: String = "自定义协程取消" ) : CancellationException(message)
在协程内抛出该异常的效果和原生CancellationException完全一致:会中断当前协程执行,被框架消费,不会产生崩溃或额外错误日志。
非协程场景下的非崩溃异常
不存在可以任意抛出、不被捕获就不会触发崩溃的特殊异常。非协程场景下,如果异常没有被就近的try/catch捕获,最终一定会走到线程的UncaughtExceptionHandler,系统默认实现会直接终止应用。如果要实现类似效果,只能在对应代码逻辑中手动添加捕获逻辑,不推荐通过自定义全局未捕获异常处理器过滤特定异常的方式实现,很容易导致应用进入不可预期的异常状态。
重要注意事项:不要在协程中捕获
CancellationException后直接吞掉不重抛,否则会破坏协程的取消机制,导致资源泄漏、逻辑执行不符合预期:// 错误写法:捕获后不重抛,协程取消失效 coroutineScope.launch { try { doLongRunningTask() } catch (e: CancellationException) { Log.d("test", "协程被取消") // 未重新抛出异常,协程会继续执行后续逻辑 } doNextTask() // 协程取消后该行仍会执行 } // 正确写法:处理完必要逻辑后重新抛出异常 coroutineScope.launch { try { doLongRunningTask() } catch (e: CancellationException) { Log.d("test", "协程被取消") throw e // 重新抛出,让框架正常完成取消流程 } }
内容的提问来源于stack exchange,提问作者lexchronos
相关产品推荐
相关产品推荐

