[协程]-[详解]-[协程作用域]-viewModelScope

概念

viewModelScope 跟 lifecycleScope 很像, 也会在特定时候自己取消自己, 确保不会出现内存泄漏

viewModelScope 是一个协程作用域, 是 ViewModel 的一个扩展属性, 在 ViewModel 销毁时会自己取消自己.


使用

Kotlin 复制代码
class MyViewModel : ViewModel() {
    fun loadData() {
        // 直接启动协程,无需担心 Activity 旋转或销毁导致的泄露
        viewModelScope.launch {
            val result = repository.getData() // 挂起函数
            _uiState.value = result
        }
    }
}

源码解析

ViewModel.viewModelScope

可以看到, viewModelScope 并未使用类似 LifecycleScope 的CAS自旋安全构建单例的方式, 而是直接通过加 synchronized 锁方式并发安全构建, 并最终挂载到对象Tag集合中.

上下文倒与LifecycleScope相同, 都使用了SupervisorJob + Dispatchers.Main.immediate

Kotlin 复制代码
public val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope: CoroutineScope? = this.getTag(JOB_KEY)
        if (scope != null) return scope
        
        // 如果没有缓存,则创建一个新的
        return setTagIfAbsent(
            JOB_KEY,
            CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
        )
    }

与 LifecycleScope 不一样的是, 它不依赖生命周期的监听. 而是专门写了 ViewModel 对象销毁机制. "ViewModel 在执行 clear() 时, 会自动遍历并回调 Scope 的 close() 方法",

具体代码如何, 让我们扒一扒 CloseableCoroutineScope...

CloseableCoroutineScope

CloseableCoroutineScope 代码很简单, 就是一个实现了Closeable接口的协程作用域(CoroutineScope), 他实现了close 方法, 并在方法中执行了cancel

Kotlin 复制代码
internal class CloseableCoroutineScope(
    context: CoroutineContext
) : Closeable, CoroutineScope {
    // 持有并暴露协程上下文
    override val coroutineContext: CoroutineContext = context

    /**
     * 当 ViewModel 销毁,触发 mBagOfTags 遍历时,
     * 会调用此 close() 方法。
     */
    override fun close() {
        coroutineContext.cancel()
    }
}

那 close 方法又是怎么保证被执行的呢?

原来, ViewModel 有自己的销毁机制, 在执行ViewModel.clear时, 会遍历一个叫做mBagOfTags的集合 (viewModelScope构建时, 就会将CloseableCoroutineScope构建后塞入这个集合), 如果类型是Closeable的, 就会执行Closeable.close()

具体ViewModel.clear代码如下

Kotlin 复制代码
/** ViewModel.kt */
@MainThread
final void clear() {
    mCleared = true;
    
    // viewModelScope 在构建时, 会把CloseableCoroutineScope构建后
    // 塞到ViewModel.mBagOfTags中, 在执行ViewModel.clear时会遍历这个mBagOfTags
    // 如果发现 item 是 Closeable 类型, 就执行close
    synchronized (mBagOfTags) {
        for (Object value : mBagOfTags.values()) {
            // 2. 关键:在这里执行 closeNext()
            closeWithRuntimeException(value);
        }
    }
    onCleared(); // 开发者重写的那个方法
}

/** ViewModel.java */
private static void closeWithRuntimeException(Object obj) {
    if (obj instanceof Closeable) {
        try {
            ((Closeable) obj).close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

自此 viewModelScope 从如何构建到如何自动取消的流程, 都基于源码解析完了, 接下来我们做个总结


总结

viewModelScope 是 ViewModel 的扩展属性, 实际上是 CloseableCoroutineScope 类对象, CloseableCoroutineScope 组合了 Closeable 和 CoroutineScope 的能力

在ViewModel中使用viewModelScope时, 系统会结合 synchronized锁 并发安全地构建CloseableCoroutineScope 对象, 并存入ViewModel中一个名为 mBagOfTags 的 Map集合中, 在最终执行 ViewModel.clear() 时会遍历这个Map, 当item为Closeable类型时, 执行close, 然后内部直接cancel掉这个协程作用域.

这样一来, 就实现了viewModelScope 的自动取消能力了.

相关推荐
1024+19 分钟前
YOLO 转 RKNN 识别率降低调优操作手册(新手篇)
android·yolo·kotlin
帅次13 小时前
Kotlin 与 Java 互操作:混合工程里的平台类型与 API 边界
java·开发语言·kotlin·suspend·nullable
plainGeekDev18 小时前
kapt 替换为 KSP
android·java·kotlin
蜡台18 小时前
通过Gradle脚本声明更改Java变量
android·java·开发语言·python·kotlin·gradle·groovy
杉氧1 天前
Ktor 全栈之路 (5):JWT 认证全流程实战 —— 打造安全通信闭环
android·架构·kotlin
alexhilton1 天前
MVI模式的完整历史、误解和现代Android范式
android·kotlin·android jetpack
pengyu2 天前
【Kotlin 协程修仙录 · 金丹境 · 后阶】 | 异常防火墙:supervisorScope 与异常隔离的终极奥义
android·kotlin
夏沫琅琊2 天前
Jetpack Compose + 原生 PDF API 从零搭一个 Android 工具应用 (一)
android·pdf·kotlin
疏狂难除3 天前
JetBrains IDE插件开发教程(五)——File Header
ide·kotlin
Kapaseker3 天前
Android CLI 完全指南
android·kotlin