概念
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 的自动取消能力了.