使用
在 ViewModel 中使用协程时,一种写法是在 ViewModel 里实现 CoroutineScope 接口,然后在 onCleared() 方法里取消 job 。
在 AndroidX Lifecycle v2.1.0 中,在 ViewModel 里引入了 viewModelScope ,既不需要额外的实现,也会随着 ViewModel 的销毁而销毁。
viewModelScope 的使用很简单,在 ViewModel 内部直接调用即可。
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
launch ,async 的调用方式和 CoroutineScope 中使用一致。
默认是 Dispatchers.Main
原理
viewModelScope 是 ViewModel 的一个扩展属性,定义如下:
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))
}
首次使用时,会调用 setTagIfAbsent() , 会根据 Tag 将生成的 Scope 保存到一个 HashMap 里。后续使用时通过 Tag 取之前创建的 Scope 。
最开始是保存在
ConcurrentHashMap里,但后面换成了HashMap,原因是前者可能丢失旧的apis的值。
当 ViewModel 销毁时,它会调用 clear() 方法,在该方法内部,会清理掉 HashMap 里的所有值。
@MainThread
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
清理的要求是里面保存的对象必须实现了 Closeable ,在 closeWithRuntimeException() 里是通过调用 Closeable 接口的 close() 来实现的。
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
参考文章: