Kotlin协程SupervisorScope在Android中为何因子Job异常崩溃?
SupervisorScope在Android中因子协程未捕获异常崩溃的解决方法
我知道Kotlin协程的SupervisorScope特性是:当某个子Job被取消或抛出异常时,不会取消其他兄弟Job。这个逻辑在IntelliJ里运行正常,但在Android项目中执行时,会抛出异常导致应用崩溃。调用代码是通过Compose按钮点击,用协程Scope触发以下方法:
suspend fun asyncJobLaunchWithTryCatchSample() { supervisorScope { val nameJob = launch { Log.i(TAG, "asyncLaunchWithTryCatchSample: nameJob Started") val name = getName()//fake api call with delay of 1 second Log.i(TAG, "asyncLaunchWithTryCatchSample: nameJob completed $name") } val ageJob1 = launch { Log.i(TAG, "asyncLaunchWithTryCatchSample: ageJob1 Started") throw Exception("dsadasd") } val ageJob2 = launch {//fake api call with delay of 1 second Log.i(TAG, "asyncLaunchWithTryCatchSample: ageJob2 Started") val age = getAgeNull() Log.i(TAG, "asyncLaunchWithTryCatchSample: ageJob2 Completed $age") } joinAll(nameJob,ageJob1,ageJob2) Log.i(TAG, "asyncLaunchWithTryCatchSample: completed") } }
问题原因
SupervisorScope确实不会因为某个子协程异常就取消其他兄弟协程,但未捕获的异常仍然会向上传递到父协程作用域。在Android中,默认的协程异常处理机制会把未捕获的异常视为致命错误,直接触发应用崩溃;而IntelliJ的默认异常处理只是打印日志,不会终止进程,所以看起来逻辑正常。
解决方案
要解决这个问题,有两种常见实现方式:
1. 在抛出异常的子协程内部捕获异常
直接在可能抛异常的launch块里添加try-catch,阻止异常向上传递:
val ageJob1 = launch { try { Log.i(TAG, "asyncLaunchWithTryCatchSample: ageJob1 Started") throw Exception("dsadasd") } catch (e: Exception) { Log.e(TAG, "ageJob1执行失败", e) } }
2. 给协程设置统一的异常处理器
如果需要统一处理子协程的未捕获异常,可以在启动包含supervisorScope的协程时,指定CoroutineExceptionHandler:
// 定义全局异常处理器 val exceptionHandler = CoroutineExceptionHandler { _, throwable -> Log.e(TAG, "捕获到协程异常", throwable) } // Compose按钮点击时的调用逻辑 scope.launch(exceptionHandler) { asyncJobLaunchWithTryCatchSample() }
也可以给单个launch块单独指定异常处理器:
val ageJob1 = launch(exceptionHandler) { Log.i(TAG, "asyncLaunchWithTryCatchSample: ageJob1 Started") throw Exception("dsadasd") }
修改后,ageJob1抛出的异常会被处理,不会导致应用崩溃,同时nameJob和ageJob2能正常执行完成。
内容的提问来源于stack exchange,提问作者Suresh Sivalingam
相关产品推荐
相关产品推荐

