Appearance
Kotlin 协程 Scope 作用域、Job 状态机与异常隔离
回到总览:并发与运行时底座
相关模块:Kotlin 协程 Dispatchers、CoroutineContext 与构建器 · Kotlin 协程 Mutex 互斥锁、共享变量与无锁并发
一句话定义
Kotlin 协程提出了 结构化并发 (Structured Concurrency) 原理:所有协程必须在固定的 CoroutineScope 作用域中启动;通过 Job 状态机管理父子协程的生命周期取消,并通过 SupervisorJob 实现子协程异常的精准隔离防护。
代码索引
对应 Lab:coroutine_execution_flow
为什么需要
- 为什么在 Android 开发中盲目使用
GlobalScope.launch是极其严重的内存泄漏隐患?- 一句话答:
GlobalScope的生命周期与整个 App 进程一样长;如果在 Activity 中用它启动耗时任务,页面销毁后协程不会自动取消,强持 Activity 对象引发内存泄漏。
- 一句话答:
- 默认情况下子协程抛出未捕获异常,为什么会导致父协程以及兄弟协程全被取消?
- 一句话答:这是双向异常传播机制:子协程崩溃会将异常向上传递给父协程,父协程收到后取消自身,并向下取消所有其他兄弟子协程;如果需要隔离异常(子崩溃不影响兄弟),必须使用
SupervisorJob或supervisorScope。
- 一句话答:这是双向异常传播机制:子协程崩溃会将异常向上传递给父协程,父协程收到后取消自身,并向下取消所有其他兄弟子协程;如果需要隔离异常(子崩溃不影响兄弟),必须使用
底层机制
1. Job 状态机演进图
2. 普通 Job vs SupervisorJob 异常传播对比
图注补充:2. 向下取消所有兄弟;1. 异常被 Supervisor 拦截隔离。
Android / Flutter / Web / Backend 对照
| 异常与作用域 | Kotlin 协程 | Dart (Flutter) | Go 语言 |
|---|---|---|---|
| 生命周期作用域 | viewModelScope / lifecycleScope | State.dispose 手动 cancel | context.WithCancel(ctx) |
| 异常隔离机制 | SupervisorJob() / supervisorScope | Isolate 之间默认彻底隔离 | recover() 防 panic 崩溃 |
常见场景
1. 使用 supervisorScope 隔离局部异常
kotlin
suspend fun fetchDashboardData() = supervisorScope {
// 两个子请求并行发起
val userDeferred = async { fetchUserProfile() }
val bannerDeferred = async { fetchBanners() } // 即使 banner 失败抛异常
val user = try { userDeferred.await() } catch (e: Exception) { null }
val banner = try { bannerDeferred.await() } catch (e: Exception) { null }
DashboardUiState(user, banner) // 保证 user 数据依然能正常展示!
}对应实验
各章节内已嵌入跳转链接;此处汇总全部 Lab:
| Lab | 说明 |
|---|---|
| coroutine_execution_flow | Main → IO → Main 线程切换、async/await、状态机等效执行流验证 |
复习检查题
在 Kotlin 协程中,
Job与SupervisorJob处理子协程异常的核心区别是什么?答:默认的
Job采用双向异常传播机制:任何一个子协程发生未捕获异常时,都会向上抛给父 Job,导致父 Job 取消自身并向下取消所有其他兄弟子协程;而SupervisorJob实现了异常隔离,某个子协程崩溃不会影响父 Job,也不会取消其他同级兄弟子协程。为什么在 ViewModel 中推荐使用
viewModelScope启动协程?答:因为
viewModelScope绑定了 ViewModel 的生命周期。当 ViewModel 对应的页面销毁(触发onCleared())时,viewModelScope会自动调用内部 Job 的cancel(),递归取消该作用域下启动的所有耗时协程,彻底避免了后台无用任务浪费 CPU/网络资源及内存泄漏。
速记
- 结构化并发:绝不用 GlobalScope,Activity/ViewModel 使用自带 Scope。
- 异常隔离:多并行任务用
supervisorScope,防止单个子任务失败干掉整个页面。 - 取消协作:协程取消是协作式的,耗时循环内需检测
ensureActive()或yield()。