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

相关推荐
hnlgzb7 分钟前
请详细解释一下MVVM这个设计模型
android·kotlin·android jetpack·compose
夏沫琅琊2 小时前
Kotlin 基础(一)
kotlin
夏沫琅琊4 小时前
Android API 发送短信技术文档
android·kotlin
夏沫琅琊4 小时前
Android 彩信导出技术文档
android·kotlin
hnlgzb16 小时前
安卓app kotlin语法,Hilt是什么东西?
android·开发语言·kotlin
simplepeng21 小时前
Kotlin 协程桥接(suspendCoroutine):将任意基于回调的 Android API 转换为挂起函数
kotlin
zhangphil1 天前
Kotlin高阶函数作为参数与Java普通接口interface等效性
java·kotlin
Kapaseker1 天前
千万不要以为你搞懂了 var 和 val
android·kotlin