Kotlin协程中CoroutineExceptionHandler与try-catch无法捕获异常
Kotlin协程async异常捕获失效问题排查与解决
问题描述
使用Kotlin协程时,async块内抛出的异常既无法被CoroutineExceptionHandler捕获,也无法被try-catch块捕获。复现代码如下:
import kotlinx.coroutines.* fun log(message: String) { println("[${Thread.currentThread()}] $message") } fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val scope = CoroutineScope(handler) val task = scope.async() { log("throw exception") throw RuntimeException("Exception") } scope.launch(handler) { try { task.await() } catch (e: Exception) { log("catch exception: ${e.message}") } delay(1000) log("finish await") } delay(2000) }
运行后控制台仅输出:
[Thread[DefaultDispatcher-worker-1,5,main]] throw exception
程序无异常捕获日志输出便直接结束。
异常未被捕获的核心原因
普通Job作用域下的异常立即传播:你使用的
CoroutineScope(handler)默认绑定普通Job,async协程抛出异常时,会立即将异常传播到父作用域的Job,导致整个作用域标记为失败状态,并取消所有子协程(包括后续创建的launch协程)。因此scope.launch里的try-catch和await()逻辑根本没有执行的机会。CoroutineExceptionHandler未触发的原因:虽然你给作用域配置了异常处理器,但由于
runBlocking作用域和自定义scope相互独立,且未等待自定义scope的协程完成,处理器的输出可能在程序结束前未被执行或缓冲,导致你看不到捕获日志。
正确的异常捕获方法
方法1:用SupervisorJob隔离子协程异常
SupervisorJob会阻止子协程的异常传播到父作用域,也不会影响其他子协程的执行。此时async的异常只会在调用await()时抛出,try-catch可正常捕获:
import kotlinx.coroutines.* fun log(message: String) { println("[${Thread.currentThread()}] $message") } fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } // 使用SupervisorJob创建隔离作用域 val scope = CoroutineScope(SupervisorJob() + handler) val task = scope.async { log("throw exception") throw RuntimeException("Exception") } scope.launch { try { task.await() } catch (e: Exception) { log("catch exception: ${e.message}") } delay(1000) log("finish await") } delay(2000) scope.cancel() }
方法2:在同一协程内调用await()并捕获
如果不需要跨协程调用await(),可直接在创建async的协程内执行await(),此时try-catch能直接捕获异常:
import kotlinx.coroutines.* fun log(message: String) { println("[${Thread.currentThread()}] $message") } fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val scope = CoroutineScope(handler) scope.launch { val task = async { log("throw exception") throw RuntimeException("Exception") } try { task.await() } catch (e: Exception) { log("catch exception: ${e.message}") } delay(1000) log("finish await") } delay(2000) }
方法3:等待作用域协程完成触发处理器
若想让CoroutineExceptionHandler捕获异常,需等待自定义scope的协程完成,确保处理器有机会执行:
import kotlinx.coroutines.* fun log(message: String) { println("[${Thread.currentThread()}] $message") } fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val scope = CoroutineScope(handler) val task = scope.async { log("throw exception") throw RuntimeException("Exception") } // 等待async协程完成,触发作用域的异常处理器 task.join() delay(2000) }
内容的提问来源于stack exchange,提问作者CoXier
相关产品推荐
相关产品推荐

