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

相关推荐
心之伊始5 小时前
Spring Boot Actuator + Micrometer 自定义业务指标:不只是健康检查
java·架构·源码分析·csdn
zhangphil6 小时前
Android将ImageView显示的图原样取出转换为Bitmap,Kotlin
android·kotlin
plainGeekDev6 小时前
CountDownTimer → Flow
android·java·kotlin
消失的旧时光-19437 小时前
Kotlin 协程设计思想(七):为什么 Kotlin 要设计 SupervisorJob 和 supervisorScope?
android·开发语言·kotlin
JohnnyDeng948 小时前
【Android】RecyclerView性能优化与缓存机制:从卡顿到丝滑的完整指南
android·性能优化·kotlin·mvvm
zfoo-framework8 小时前
kotlin中体会到一些比较好用的点
android·开发语言·kotlin
我是唐青枫8 小时前
Kotlin also 详解:附加操作、链式调试与实战示例
kotlin
alexhilton19 小时前
AppFunctions:让你的Android应用更容易被AI智能体发现
android·kotlin·android jetpack
赏金术士19 小时前
Android 组件化概念和特征
android·kotlin·组件化
我命由我123451 天前
Android 开发,FragmentPagerAdapter 的 isViewFromObject 方法问题
android·java-ee·kotlin·android studio·android jetpack·android-studio·android runtime