[协程]-[详解]-[协程作用域]-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 的自动取消能力了.

相关推荐
Fate_I_C2 小时前
Android DataBinding数据绑定表达式、双向绑定
android·kotlin·databinding
jinanwuhuaguo5 小时前
OpenClaw范式深度剖析:从技术突破到安全治理的系统性研究(第二篇)
开发语言·人工智能·安全·架构·kotlin·openclaw
Fate_I_C6 小时前
Android Navigation组件核心问题深度解析
android·kotlin·navigation
Kapaseker6 小时前
Kotlin 的 init 到底咋回事儿?
android·kotlin
Fate_I_C6 小时前
Android Navigation的使用说明
android·kotlin·navigation
高林雨露6 小时前
Java开发转kotlin
java·kotlin
Fate_I_C1 天前
ViewModel 的生命周期与数据保持
android·kotlin
Fate_I_C1 天前
Kotlin函数一
android·开发语言·kotlin
pengyu1 天前
【Kotlin 协程修仙录 · 炼气境 · 初阶】 | 感受天地灵气,写出第一个挂起函数
android·kotlin
molong9311 天前
SIM 卡监听(电话监听)
android·学习·kotlin