Kotlin 协程作用域源码剖析

GlobalScope、MainScope、lifecycleScope、viewModelScope 四者的实现原理与正确使用姿势

1. viewModelScope

kotlin 复制代码
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            // 在这里启动的协程会自动绑定 ViewModel 生命周期
            // ViewModel 被清理时,协程会被取消
        }
    }
}

源码分析

kotlin 复制代码
// 用 ViewModel 的 tag 机制缓存 CoroutineScope,保证一个 ViewModel 实例只有一个 Scope
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
​
val ViewModel.viewModelScope: CoroutineScope
    get() {
        // 1. 先从 ViewModel 的 Tag 缓存中读取
        val scope: CoroutineScope? = this.getTag(JOB_KEY)
        if (scope != null) {
            return scope
        }
        // 2. 首次访问时创建:使用 SupervisorJob,子协程失败不会相互影响
        //    Dispatchers.Main.immediate 保证 launch 块立即在当前主线程执行(若已在主线程)
        return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
    }
​
// 包装一层 CloseableCoroutineScope 是为了让 ViewModel 在 onCleared() 时能关闭并取消 Scope
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
​
    override fun close() {
        // 当 ViewModel 被清除时,调用此方法取消整个 Job 树
        // 所有未完成的子协程会收到取消信号,避免内存泄漏
        coroutineContext[Job]?.cancel()
    }
}

要点: viewModelScope 与 ViewModel 生命周期绑定;子协程之间通过 SupervisorJob 隔离失败;ViewModel.onCleared() 会触发 CloseableCoroutineScope.close() 从而取消所有协程。

2. lifecycleScope

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            // 与 Activity / Fragment 生命周期绑定
            // 在 ON_DESTROY 时自动取消协程
        }
    }
}

源码分析

kotlin 复制代码
// 伪代码逻辑展示核心机制
internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
​
    init {
        // 关键:注册生命周期观察者,监听 DESTROY 事件
        lifecycle.addObserver(this)
    }
​
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            // 当接收到 DESTROY 事件时,自动取消协程
            // 避免在 Activity/Fragment 销毁后继续执行 UI 更新或耗时操作
            coroutineContext[Job]?.cancel()
        }
    }
}
​
val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() {
        // 每个 LifecycleOwner 只创建一次 Scope,后续复用
        return findOrCreateScope()
    }

要点: lifecycleScope 监听 Lifecycle.Event.ON_DESTROY;在页面销毁时自动取消协程,防止空指针和内存泄漏。

3. MainScope

kotlin 复制代码
// 适用于不依赖 Activity/Fragment/ViewModel 的顶层协程管理场景
private val scope = MainScope()
​
scope.launch {
    // 在主线程执行的协程
}
​
// MainScope 本质上就是 SupervisorJob + Dispatchers.Main
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)

要点: MainScope() 创建了一个挂在主线程的顶层作用域,需要手动调用 scope.cancel() 结束生命周期,否则可能造成泄漏。

4. GlobalScope

kotlin 复制代码
// 不推荐直接使用,协程生命周期跟随整个应用进程
GlobalScope.launch { }
​
@DelicateCoroutinesApi
public object GlobalScope : CoroutineScope {
    // EmptyCoroutineContext 表示没有绑定 Job 和调度器
    // 使用时会自动创建 Job,但无法从外部统一取消
    override val coroutineContext: CoroutineContext
        get() = EmptyCoroutineContext
}

要点: GlobalScope 是全局单例,没有统一取消入口;仅适合需要在整个应用生命周期内运行的后台任务,通常应避免使用,否则容易出现不可控的协程泄漏。

总结

作用域 生命周期绑定 是否需要手动取消 推荐场景
viewModelScope ViewModel 否 在 ViewModel 中发起网络请求、数据库操作
lifecycleScope Activity / Fragment 否 在 UI 层随页面创建/销毁的轻量任务
MainScope() 手动管理 是 类级别、不依赖生命周期组件的顶层协程
GlobalScope 应用进程 否(但无法统一取消) 原则上避免,仅用于特殊全局后台任务

一句话:有生命周期就绑生命周期,没生命周期再考虑 MainScope,尽量不用 GlobalScope。

相关推荐
蓝速科技5 小时前
政务自助终端信创选型与无人值守落地方案
android·大数据·数据库·人工智能·科技·技术分享·政务
硫酸锌016 小时前
在手机上运行Python增量备份手机数据到PC电脑
android·windows·python
花开路口6 小时前
线程安全完全指南:从 Java 到 Kotlin,一文吃透并发编程
java·kotlin
hai_android7 小时前
子 View 被压小时,如何向父容器"告状"?——MEASURED_STATE_TOO_SMALL 向上传递机制
android·kotlin·android jetpack
AFinalStone7 小时前
Android 7 多用户源码解析(七)系统服务的多用户感知处理
android·系统架构·多用户
怪奇云呼军7 小时前
从 ElevenLabs 看工具调用:闪电智能 Voice Agent 的企业集成验收设计
android·大数据·运维·服务器·网络·人工智能·kotlin
157092511348 小时前
Android 架构模式详解:从 MVC、MVP 到 MVVM
android·架构·mvc
aaajj9 小时前
Android手机连接鼠标情况
android
其实防守也摸鱼9 小时前
SQL注入实验笔记
android·运维·数据库·安全·oracle·自动化
找茬小王子10 小时前
Android原生Monkey工具的使用
android·monkey