跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
Kotlin协程异常处理
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{Note|本文适用于Kotlin协程初学者及需要系统了解异常处理的开发者。假设读者已掌握Kotlin基础语法和协程基本概念。}} = Kotlin协程异常处理 = == 概述 == '''协程异常处理'''是结构化并发中的核心机制,用于管理协程执行过程中出现的错误。与线程不同,协程通过父子层级关系传播异常,提供更可控的错误处理方式。Kotlin使用`CoroutineExceptionHandler`和`try/catch`等机制实现多层级的异常管理。 关键特性: * 自动传播:未捕获的异常会取消父协程及同级协程 * 结构化处理:通过Job层级实现异常传播路径控制 * 多处理策略:支持集中处理(`CoroutineExceptionHandler`)和局部处理(`try/catch`) == 异常传播机制 == === 层级传播原理 === <mermaid> graph TD A[根协程] --> B[子协程1] A --> C[子协程2] B --> D[孙子协程] D -->|抛出异常| B B -->|传播异常| A C -->|被取消| A </mermaid> 异常传播遵循以下规则: 1. 子协程抛出未捕获异常时,首先取消自己的所有子协程 2. 异常向上传递给父协程 3. 父协程取消所有其他子协程 4. 继续向上传播直到根协程或捕获点 === 传播类型对比 === {| class="wikitable" |+ 异常传播行为对比 ! 启动方式 !! 自动传播 !! 处理方式 |- | launch | 是 | 可通过`CoroutineExceptionHandler`捕获 |- | async | 否 | 需通过`await()`显式处理 |} == 基础处理方式 == === try/catch块 === 最直接的异常处理方式,适用于同步代码和挂起函数: <syntaxhighlight lang="kotlin"> fun main() = runBlocking { try { launch { throw ArithmeticException("计算错误") } } catch (e: Exception) { println("捕获到异常: ${e.message}") // 不会执行! } delay(1000) // 给协程时间执行 } </syntaxhighlight> {{Warning|直接使用`try/catch`无法捕获`launch`内部的异常,因为协程是异步执行的}} 正确用法: <syntaxhighlight lang="kotlin"> suspend fun riskyOperation() { throw IOException("文件操作失败") } fun main() = runBlocking { try { riskyOperation() // 在挂起函数中使用 } catch (e: IOException) { println("处理IO异常: ${e.message}") } } </syntaxhighlight> === CoroutineExceptionHandler === 全局异常处理器,适用于根协程: <syntaxhighlight lang="kotlin"> val handler = CoroutineExceptionHandler { _, exception -> println("全局处理: ${exception.javaClass.simpleName} - ${exception.message}") } fun main() = runBlocking { val scope = CoroutineScope(Job() + handler) scope.launch { throw NullPointerException("空指针异常") }.join() } </syntaxhighlight> 输出: <pre> 全局处理: NullPointerException - 空指针异常 </pre> {{Note|`CoroutineExceptionHandler`仅在以下情况生效: * 用于根协程(直接子级为`CoroutineScope`或`supervisorScope`的协程) * 与`launch`配合使用(不适用于`async`)}} == 高级处理策略 == === SupervisorJob模式 === 使用`SupervisorJob`可阻止异常传播到同级协程: <mermaid> graph TD A[SupervisorJob] --> B[子协程1] A --> C[子协程2] B -->|抛出异常| B C -->|继续运行| A </mermaid> 实现示例: <syntaxhighlight lang="kotlin"> 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() } </syntaxhighlight> 输出: <pre> 健康协程 0 健康协程 1 健康协程 2 健康协程 3 健康协程 4 </pre> === async/await异常处理 === `async`协程需通过`await()`捕获异常: <syntaxhighlight lang="kotlin"> fun main() = runBlocking { val deferred = async { throw CustomException("异步操作失败") } try { deferred.await() } catch (e: CustomException) { println("捕获异步异常: ${e.message}") } } </syntaxhighlight> == 复合场景处理 == === 多协程异常合并 === 当多个子协程抛出异常时,Kotlin会保留第一个异常并通过`suppressed`属性附加后续异常: <syntaxhighlight lang="kotlin"> 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() } } </syntaxhighlight> === 资源清理保证 === 使用`try/finally`或`use`确保资源释放: <syntaxhighlight lang="kotlin"> suspend fun writeToFile() { val resource = acquireResource() try { resource.use { delay(500) // 模拟操作 throw IOException("写入失败") } } finally { println("清理临时文件") } } </syntaxhighlight> == 最佳实践 == 1. '''层级设计原则''': * 对不可恢复错误使用全局处理器 * 对业务异常使用局部`try/catch` 2. '''恢复策略''': * 实现重试机制时结合`retryWhen`: <syntaxhighlight lang="kotlin"> 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("未知错误") } </syntaxhighlight> 3. '''监控集成''': * 在`CoroutineExceptionHandler`中添加日志和监控上报 == 常见问题 == {{Q&A |问题 = 为什么我的CoroutineExceptionHandler没有触发? |解答 = 检查是否满足三个条件:(1)用于根协程 (2)与launch配合 (3)未使用SupervisorJob隔离 }} {{Q&A |问题 = 如何区分取消(Cancellation)和真实异常? |解答 = 检查异常是否为`CancellationException`子类,协程取消会抛出该类型异常 }} == 数学表达 == 异常传播的取消范围可以用集合表示: 设协程树为<math>T = (V, E)</math>,其中: * <math>v_e \in V</math> 为抛出异常的节点 * 受影响节点集 <math>A = \{ v \in V \mid v \text{ 是 } v_e \text{ 的祖先或兄弟} \}</math> == 总结 == Kotlin协程异常处理的核心要点: 1. 理解结构化并发的传播机制 2. 根据场景选择局部处理或全局处理 3. 重要资源必须保证清理 4. 合理使用监督作用域(SupervisorJob)隔离异常 通过结合`try/catch`、`CoroutineExceptionHandler`和协程构建器策略,可以构建健壮的异步错误处理系统。 [[Category:编程语言]] [[Category:Kotlin]] [[Category:Kotlin协程]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
该页面使用的模板:
模板:Mbox
(
编辑
)
模板:Note
(
编辑
)
模板:Q&A
(
编辑
)
模板:Warning
(
编辑
)
模块:Arguments
(
编辑
)
模块:Message box
(
编辑
)
模块:Message box/ambox.css
(
编辑
)
模块:Message box/configuration
(
编辑
)
模块:Yesno
(
编辑
)