跳转到内容

Kotlin协程异常处理

来自代码酷

模板:Note

Kotlin协程异常处理[编辑 | 编辑源代码]

概述[编辑 | 编辑源代码]

协程异常处理是结构化并发中的核心机制,用于管理协程执行过程中出现的错误。与线程不同,协程通过父子层级关系传播异常,提供更可控的错误处理方式。Kotlin使用`CoroutineExceptionHandler`和`try/catch`等机制实现多层级的异常管理。

关键特性:

  • 自动传播:未捕获的异常会取消父协程及同级协程
  • 结构化处理:通过Job层级实现异常传播路径控制
  • 多处理策略:支持集中处理(`CoroutineExceptionHandler`)和局部处理(`try/catch`)

异常传播机制[编辑 | 编辑源代码]

层级传播原理[编辑 | 编辑源代码]

graph TD A[根协程] --> B[子协程1] A --> C[子协程2] B --> D[孙子协程] D -->|抛出异常| B B -->|传播异常| A C -->|被取消| A

异常传播遵循以下规则: 1. 子协程抛出未捕获异常时,首先取消自己的所有子协程 2. 异常向上传递给父协程 3. 父协程取消所有其他子协程 4. 继续向上传播直到根协程或捕获点

传播类型对比[编辑 | 编辑源代码]

异常传播行为对比
启动方式 自动传播 处理方式
是 | 可通过`CoroutineExceptionHandler`捕获
否 | 需通过`await()`显式处理

基础处理方式[编辑 | 编辑源代码]

try/catch块[编辑 | 编辑源代码]

最直接的异常处理方式,适用于同步代码和挂起函数:

fun main() = runBlocking {
    try {
        launch {
            throw ArithmeticException("计算错误")
        }
    } catch (e: Exception) {
        println("捕获到异常: ${e.message}") // 不会执行!
    }
    delay(1000) // 给协程时间执行
}

页面模块:Message box/ambox.css没有内容。

正确用法:

suspend fun riskyOperation() {
    throw IOException("文件操作失败")
}

fun main() = runBlocking {
    try {
        riskyOperation() // 在挂起函数中使用
    } catch (e: IOException) {
        println("处理IO异常: ${e.message}")
    }
}

CoroutineExceptionHandler[编辑 | 编辑源代码]

全局异常处理器,适用于根协程:

val handler = CoroutineExceptionHandler { _, exception ->
    println("全局处理: ${exception.javaClass.simpleName} - ${exception.message}")
}

fun main() = runBlocking {
    val scope = CoroutineScope(Job() + handler)
    scope.launch {
        throw NullPointerException("空指针异常")
    }.join()
}

输出:

全局处理: NullPointerException - 空指针异常

模板:Note

高级处理策略[编辑 | 编辑源代码]

SupervisorJob模式[编辑 | 编辑源代码]

使用`SupervisorJob`可阻止异常传播到同级协程:

graph TD A[SupervisorJob] --> B[子协程1] A --> C[子协程2] B -->|抛出异常| B C -->|继续运行| A

实现示例:

fun main() = runBlocking {
    val supervisor = SupervisorJob()
    val scope = CoroutineScope(coroutineContext + supervisor)
    
    scope.launch {
        delay(100)
        throw RuntimeException("失败协程")
    }
    
    scope.launch {
        repeat(5) { i ->
            println("健康协程 $i")
            delay(200)
        }
    }.join()
}

输出:

健康协程 0
健康协程 1
健康协程 2
健康协程 3
健康协程 4

async/await异常处理[编辑 | 编辑源代码]

`async`协程需通过`await()`捕获异常:

fun main() = runBlocking {
    val deferred = async {
        throw CustomException("异步操作失败")
    }
    
    try {
        deferred.await()
    } catch (e: CustomException) {
        println("捕获异步异常: ${e.message}")
    }
}

复合场景处理[编辑 | 编辑源代码]

多协程异常合并[编辑 | 编辑源代码]

当多个子协程抛出异常时,Kotlin会保留第一个异常并通过`suppressed`属性附加后续异常:

fun main() {
    val handler = CoroutineExceptionHandler { _, e ->
        println("主异常: ${e.message}")
        e.suppressed.forEach { 
            println("附加异常: ${it.message}") 
        }
    }
    
    runBlocking {
        val scope = CoroutineScope(handler)
        scope.launch {
            launch { throw IOException("IO错误") }
            launch { throw IllegalArgumentException("参数错误") }
        }.join()
    }
}

资源清理保证[编辑 | 编辑源代码]

使用`try/finally`或`use`确保资源释放:

suspend fun writeToFile() {
    val resource = acquireResource()
    try {
        resource.use { 
            delay(500)  // 模拟操作
            throw IOException("写入失败")
        }
    } finally {
        println("清理临时文件")
    }
}

最佳实践[编辑 | 编辑源代码]

1. 层级设计原则

  * 对不可恢复错误使用全局处理器
  * 对业务异常使用局部`try/catch`

2. 恢复策略

  * 实现重试机制时结合`retryWhen`:
   suspend fun <T> retry(
       times: Int,
       delay: Long,
       block: suspend () -> T
   ): T {
       var lastEx: Exception? = null
       repeat(times) { attempt ->
           try {
               return block()
           } catch (e: Exception) {
               lastEx = e
               delay(delay)
           }
       }
       throw lastEx ?: RuntimeException("未知错误")
   }

3. 监控集成

  * 在`CoroutineExceptionHandler`中添加日志和监控上报

常见问题[编辑 | 编辑源代码]

模板:Q&A

模板:Q&A

数学表达[编辑 | 编辑源代码]

异常传播的取消范围可以用集合表示: 设协程树为T=(V,E),其中:

  • veV 为抛出异常的节点
  • 受影响节点集 A={vVv 是 ve 的祖先或兄弟}

总结[编辑 | 编辑源代码]

Kotlin协程异常处理的核心要点: 1. 理解结构化并发的传播机制 2. 根据场景选择局部处理或全局处理 3. 重要资源必须保证清理 4. 合理使用监督作用域(SupervisorJob)隔离异常

通过结合`try/catch`、`CoroutineExceptionHandler`和协程构建器策略,可以构建健壮的异步错误处理系统。