kotlin 为什么要有协程作用域
1. 协程不是“全局托管”的
在 Kotlin 中,协程是轻量级线程,但它们并不会自动和某个生命周期(Activity、ViewModel 等)绑定。
如果你直接用 GlobalScope.launch { ... }
,协程会一直运行,除非任务结束或进程退出,这就可能造成:
- 内存泄漏:Activity/Fragment 销毁了,但协程还在持有它的引用。
- 浪费资源:用户早就退出页面,但后台任务还在跑。
所以需要 CoroutineScope 来约束协程的生命周期。
2. 协程作用域的核心作用
- 管理协程生命周期:当
scope
被取消时,它内部启动的所有协程都会自动取消。 - 结构化并发:Kotlin 推崇 structured concurrency —— 所有协程必须属于某个作用域,才能更好地管理和回收。
- 避免悬挂协程:作用域保证了任务不会在不知情的情况下继续执行。
3. 常见的作用域示例
-
viewModelScope
来自lifecycle-viewmodel-ktx
,当ViewModel
销毁时自动取消协程,适合做网络请求、数据库操作。 -
lifecycleScope
来自lifecycle-runtime-ktx
,和 Activity/Fragment 生命周期绑定,适合 UI 层。 -
自定义作用域
class MyRepository {private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())fun loadData() {scope.launch {// 在 IO 线程加载数据}}fun clear() {scope.cancel() // 手动取消所有协程} }
4. 如果没有作用域会怎样?
- 协程失控:你无法控制它的生命周期。
- 很难写出可预测、稳定的程序。
- 资源泄漏的风险非常高。
📌 一句话总结:
协程作用域就是协程的“生存空间”,它决定协程何时该生、何时该亡。
用对作用域,就能安全地管理协程,避免内存泄漏和资源浪费。
在 Kotlin 协程 里之所以需要 协程作用域(CoroutineScope),主要是因为作用域解决了两个核心问题:
1. 生命周期管理
-
协程一旦启动,就会在后台运行,直到执行完或被取消。
-
如果没有作用域,协程可能会继续跑,哪怕对应的 UI(Activity/Fragment/ViewModel)已经销毁,造成 内存泄漏 和 无意义计算。
-
有了作用域,协程就和特定生命周期绑定:
lifecycleScope
→ 随 Activity/Fragment 销毁自动取消viewModelScope
→ 随 ViewModel 清理自动取消GlobalScope
→ 不会自动取消,常用于全局后台任务
2. 结构化并发(Structured Concurrency)
- 作用域规定了“谁负责”子协程的生命周期。
- 当作用域被取消时,所有子协程都会被级联取消。
- 避免了“野生协程”(orphan coroutines)无限运行的问题。
比如:
class MyViewModel : ViewModel() {fun loadData() {viewModelScope.launch {val data = repository.fetchData() // 在 ViewModel 作用域里// 如果 ViewModel 被销毁,这里的协程会自动取消}}
}
3. 异常处理
- 协程作用域还决定了异常传播路径。
- 在作用域内,如果某个子协程失败,父作用域可以感知并决定是否取消所有子任务。
- 这样让错误处理更可控,而不是“异常丢失”。
✅ 总结一句:
协程作用域就是协程的“生死边界”,用来绑定生命周期、统一管理子任务、避免内存泄漏和失控任务。