简单记录一下Kotlin 中runCatching 和 catching的使用和注意事项,记录于此,方便自己查阅。
正文
runCatching 和 catching 都是用于优雅处理异常的函数,但它们的应用场景和底层机制有本质区别。
简单来说,runCatching 是 Kotlin 标准库提供的通用异常捕获工具,而 catching 是 kotlinx.coroutines 库专为协程设计的、能安全处理协程取消机制的增强版本。
runCatching
runCatching通用异常捕获,是 Kotlin 提供的函数式异常处理方式。它会执行传入的 lambda 表达式,并将结果封装为 Result 对象。
:返回
Result.success(value)失败时:返回
Result.failure(exception)
优点:代码线性、可读性强,支持 map、recover、onSuccess、onFailure 等链式操作,非常适合构建数据处理管道。
致命缺陷:它会捕获所有异常,包括协程用于实现取消机制的 CancellationException。如果在协程中使用,会导致协程无法被正确取消,引发“僵尸协程”等严重问题。
// ✅ 在普通同步代码中安全使用
val number = runCatching { "123".toInt() }
.onFailure { it.printStackTrace() } // 务必记录日志
.getOrNull() ?: 0catching
catching:协程安全版,是 kotlinx.coroutines 库提供的扩展函数,可以看作是 runCatching 的协程安全替代品。它的核心逻辑是在捕获异常后,判断是否为 CancellationException,如果是则立即重新抛出,从而保证协程的取消信号能够正常向上传播。
使用前提:必须在项目中引入 kotlinx-coroutines-core 依赖。
import kotlinx.coroutines.catching // 注意 import 路径
// ✅ 在协程/挂起函数中安全使用
suspend fun fetchData(): Result<String> {
return catching {
// 网络请求等挂起操作
api.getData()
}.onFailure { e ->
// 这里只会处理真正的业务异常
// CancellationException 会被自动重新抛出,不会进入此块
e.printStackTrace()
}
}对比
| 特性 | runCatching | catching |
|---|---|---|
| 所属库 | Kotlin 标准库 (kotlin) | 协程库 (kotlinx.coroutines) |
| 适用场景 | 普通同步代码、非挂起函数 | 协程内部、挂起函数 (suspend) |
| 异常处理 | 捕获所有 Throwable | 捕获所有 Throwable,但会重新抛出CancellationException |
| 协程安全性 | 不安全,会破坏协程取消机制 | 安全,保证结构化并发 |
| 返回类型 | Result<T> | Result<T> |
小结
如果你在协程中必须使用 runCatching(例如,无法引入协程库或调用第三方非协程 API),请务必手动处理 CancellationException,否则将破坏协程的结构化并发。
补救措施:在 onFailure 中手动检查并重新抛出取消异常。
// ⚠️ 协程中使用 runCatching 的补救写法
runCatching {
suspendFunction()
}.onFailure { e ->
// 🔴 极其重要:必须将取消异常重新抛出!
if (e is CancellationException) throw e
// 处理其他业务异常
e.printStackTrace()
}普通同步代码:放心使用
runCatching,享受其函数式编程的便利。协程/挂起函数:优先使用
catching。这是最安全、最推荐的做法。协程中被迫使用
runCatching:必须在onFailure中手动throw CancellationException,作为安全兜底。通用原则:无论使用哪个函数,在
onFailure中务必记录异常日志,避免异常被静默吞掉,导致线上问题难以排查。
参考文章
AI

