子协程的异常传播(CoroutineExceptionHandler ):

一、代码结构:

scss 复制代码
CoroutineScope(SupervisorJob() + handler).launch {  <--- 父协程(A)

    println("父111")

    launch { <--- 子协程(B)
        throw Exception() <--- B崩了
    }

    delay(2000) <--- 这行属于 A 的代码
    println("父222")
}

真实执行流程:

  1. B 抛异常
  2. 因为是 SupervisorJob,异常不会传给父 A
  3. 但是 B 自己崩溃 → B 被取消
  4. 重点:B 取消 → 会导致父协程 A 也被取消!!!

为什么?因为:子协程 隶属于 父协程的 Job 子协程崩溃 → 子协程的 Job 取消 → 父协程的 Job 也会被取消即使你外层是 SupervisorJob!

二、真正的原因

🔥 规则 1:

SupervisorJob 只阻止:异常从子 → 父 的传播 但不阻止:子协程崩溃 → 子协程自己取消 → 导致父协程取消

🔥 规则 2:

一个协程体内的所有子协程,共享同一个 Job 子协程崩溃 → Job 取消 → 整个父体所有后续挂起函数全部中断

所以:

plaintext

scss 复制代码
delay(2000)
println("父222")

直接不执行!

3、怎么让父协程不被取消?(正确写法)

你必须给 每个子协程独立分配 SupervisorJob ()

kotlin

scss 复制代码
CoroutineScope(SupervisorJob() + exceptionHandler).launch {
    println("父111")

    // 给子协程单独加 SupervisorJob()
    launch(SupervisorJob()) {
        throw RuntimeException("出错啦")
    }

    delay(2000)
    println("父222") // ✅ 会打印!
}

这样:

  • 子协程崩
  • 子协程自己取消
  • 父协程完全不受影响
  • delay 执行
  • 父 222 打印 ✅

四、面试满分回答

  1. SupervisorJob 只能阻止异常传播,不能阻止子协程取消导致父协程取消。
  2. 子协程抛异常 → 子协程自己取消 → 子协程所在的父协程 Job 也会被取消。
  3. 所以父协程后面的 delayprintln 不会执行。
  4. 要让父协程不受影响,必须给子协程单独分配 SupervisorJob ()

五、最精简记忆口诀

子协程崩 → 子取消 → 父跟着取消(即使是 SupervisorJob)想让父不取消 → 子必须自己带 SupervisorJob ()

六、协程里分 2 种异常

1. 崩溃异常(会取消协程)

  • 直接 throw
  • 空指针、除 0、类型转换错误
  • 结果:协程挂了、Job 取消、父协程受影响

2. 业务异常(不会崩溃、不会取消协程)

  • 只是业务失败
  • 网络失败、解析失败、参数错误、数据库错误
  • try-catch 自己处理
  • 结果:协程继续跑,完全不影响父协程

3.子协程出异常,但不崩溃、不取消、不影响父协程

scss 复制代码
CoroutineScope(SupervisorJob()).launch {
    println("父协程 111")

    // 子协程
    launch {
        try {
            // 这里是业务异常,不是崩溃!
            // 比如网络请求失败、参数错误、解析错误
            throw IOException("网络请求失败!") // 普通业务异常
        } catch (e: Exception) {
            println("子协程自己处理异常:${e.message}")
        }
    }

    delay(2000)
    println("父协程 222") // ✅ 一定会打印!
}
运行结果

plaintext

复制代码
父协程 111
子协程自己处理异常:网络请求失败!
(等待2秒)
父协程 222

4.除了崩溃,还有什么异常?

你直接背这句:

业务异常,比如 IOException(网络失败)、ParseException(解析失败)、IllegalArgumentException(参数错误)、SQLException(数据库错误)等。

这些异常不会导致协程取消,只需要在子协程内部 try-catch 处理,不会影响父协程继续执行。

  1. IO 相关异常(网络 / 文件)

kotlin

scss 复制代码
// 网络请求失败
try {
    val result = apiRequest()
} catch (e: IOException) {
    println("网络连接失败,属于业务异常,程序不崩溃")
}
  1. 数据解析异常

kotlin

scss 复制代码
try {
    json.decodeFromString<User>(jsonStr)
} catch (e: JsonSyntaxException) {
    println("后端数据格式错误,解析失败,非程序崩溃")
}
  1. 参数非法异常

kotlin

kotlin 复制代码
fun checkAge(age:Int){
    try {
        if(age < 0) throw IllegalArgumentException("年龄不能为负数")
    }catch (e:IllegalArgumentException){
        println("传入参数不合法,业务拦截,不会崩")
    }
}
  1. 权限 / 状态异常

kotlin

scss 复制代码
try {
    if(!hasCameraPermission()) throw SecurityException("无相机权限")
}catch (e:SecurityException){
    println("缺少权限,提示用户即可,程序正常运行")
}
  1. 业务自定义异常(最常用)

kotlin

scss 复制代码
// 自己定义业务异常
class LoginFailException(msg:String):Exception(msg)

try {
    throw LoginFailException("账号密码错误")
}catch (e:LoginFailException){
    println("登录业务失败,界面弹窗提示,APP不崩溃")
}
  1. 数组 / 集合越界(捕获后可控)

kotlin

scss 复制代码
val list = listOf(1,2,3)
try {
    val num = list[5]
}catch (e:IndexOutOfBoundsException){
    println("下标越界,做兜底处理,流程继续")
}

5. 最关键区别(面试必问)

1. 崩溃异常

throw NullPointerException()协程取消 → 父协程被影响 → 不打印 222

2. 业务异常

try-catch 包住→ 协程不取消 → 父协程完全正常 → 打印 222

6. 子协程崩溃,防止父协程一起崩溃的方法?

  • 1、 用 try-catch包住子协程崩溃代码;
  • 2、 子协程和父协程用2个SupervisorJob();
相关推荐
不要喷香水10 小时前
00-总览与架构
kotlin·app·档案管理
hai_android10 小时前
Android 组件化开发实践
android·java·kotlin
传奇开心果编程1 天前
【Jetpack Compose基础语法学与练】第6课 TextField文本输入,字符串状态与输入交互
学习·前端框架·kotlin·android jetpack
ai2work1 天前
ch06 Jetpack Compose 入门:状态驱动 UI
kotlin
JMchen2 天前
第 12 篇|项目整合与打包发布 —— 从 Demo 到可安装 APK 的完整收官指南
kotlin·android studio
Android打工仔2 天前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin
杉氧2 天前
拒绝重复造轮子:我写了一个生产级的 Kotlin 协程与 Flow 工具库(CoroutineKit)
android·kotlin·workflow
JMchen3 天前
第 9 篇|网络请求基础 —— 从 HttpURLConnection 到 OkHttp
kotlin·android studio
hai_android4 天前
Kotlin Flow combine 源码剖析:一个 Channel 如何优雅合并多条流
android·java·kotlin
敲代码的瓦龙4 天前
Jetpack?ViewModel!!!
android·开发语言·kotlin·android-studio