Android ViewModelScope 如何自动取消协程
先看一下 ViewModel 中的 ViewModelScope 是何方神圣
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 方法进去
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);//第一次肯定为null
if (previous == null) {
mBagOfTags.put(key, newValue);//null 存储
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {//判断是否已经clear了
// It is possible that we'll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// "if the stream is already closed then invoking this method has no effect." (c)
closeWithRuntimeException(result);
}
return result;
}
可以看到 这边 会把 我们的 ViewModel 存储到 ViewModel 内的 mBagOfTags 中
这个 mBagOfTags 是
private final Map<String, Object> mBagOfTags = new HashMap<>();
这个时候 我们 viewModel 就会持有 我们 viewModelScope 的协程 作用域了。
那..这也只是 表述了 我们 viewModelScope 存在哪里而已,
什么时候清除呢?
先看一下 ViewModel 的生命周期
可以看到 ViewModel 的生命周期 会在 Activity onDestory 之后会被调用。
那...具体哪里调的?
翻看源码可以追溯到 ComponentActivity 的默认构造器内
public ComponentActivity() {
/*省略一些*/
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
可以看到内部会通对 Lifecycle 添加一个观察者,观察当前 Activity 的生命周期变更事件,如果走到了 Destory ,并且 本次 Destory 并非由于配置变更引起的,才会真正调用 ViewModelStore 的 clear 方法。
跟进 clear 方法看看
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
可以看到这个 ViewModelStore 内部实现 用 HashMap 存储 ViewModel
于是在 clear 的时候,会逐个遍历调用 clear方法
再次跟进 ViewModel 的 clear 方法
@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();
}
可以发现我们最初 存放 viewmodelScope 的 mBagOfTags
这里面的逻辑 就是对 mBagOfTags 存储的数据 挨个提取出来并且调用 closeWithRuntimeException
跟进 closeWithRuntimeException
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
该方法内会逐个判断 对象是否实现 Closeable 如果实现就会调用这个接口的 close 方法,
再回到最初 我们 viewModel 的扩展方法那边,看看我们 viewModelScope 的真正面目
internal class CloseableCoroutineScope(context: CoroutineContext)
: Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
可以明确的看到 我们的 ViewModelScope 实现了 Closeable 并且充写了 close 方法,
close 方法内的实现 会对 协程上下文进行 cancel。
至此我们 可以大致整理一下
- viewModelScope 是 ViewModel 的扩展成员,该对象是 CloseableCoroutineScope,并且实现了 Closeable 接口
- ViewModelScope 存储在 ViewModel 的 名叫 mBagOfTags 的HashMap中 啊
- ViewModel 存储在 Activity 的 ViewModelStore 中,并且会监听 Activity 的 Lifecycle 的状态变更,在ON_DESTROY 且 非配置变更引起的事件中 对 viewModelStore 进行清空
- ViewModelStore 清空会对 ViewModelStore 内的所有 ViewModel 逐个调用 clear 方法。
- ViewModel的clear方法会对 ViewModel的 mBagOfTags 内存储的对象进行调用 close 方法(该对象需实现Closeable 接口)
- 最终会会调用 我们 ViewModelScope 的实现类 CloseableCoroutineScope 的 close 方法中。close 方法会对协程进行 cancel。
作者:阿锅阿锅
链接:https://juejin.cn/post/7115406929165287438
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。