ViewModel 可以监听到当前 Activity 的 onDestory 方法来自动切断与 Activity 的联系,并且回调 ViewModel 里面的 onClear 方法,方便 使用者 去进一步去释放数据,不得不说这是一个很实用的利器
那么问题来了:
- ViewModel 是怎么去监控 Activity 的生命周期的?
- Activity 屏幕旋转后,Activity 经历了销毁和重新创建后,为什么 ViewModel 没有销毁,而是沿用之前的 ViewModel 对象呢?
下面,就带着这两个疑问,去看一下 ViewModel 的源码了
找出 ViewModel 和 Activity 的关系
创建 ViewModel 一般会用以下方法:
MainViewModel model = new ViewModelProvider(this).get(MainViewModel.class);
是的,在获取 ViewModel 实例时,并不是直接 new 的,而是使用 ViewModelProvider 来获取,那么 ViewModel 源码里面是有什么特殊地方吗?
public abstract class ViewModel {
//...省去部分代码
@MainThread
final void clear() {
mCleared = true;
//...省去部分代码
onCleared();
}
protected void onCleared() {
}
}
这里省去了一些无关的代码,我们平时能操作的只有 onClear 这个方法,这个方法调用是在 clear 中,那么 clear 方法是如何给调用的?我们使用 Android Studio 自带的 Find Useages 功能,就可以知道了:
可以看到,这个 clear 方法调用的地方只有一个,就是 ViewModelStore 中:
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
public final void clear() {
//销毁全部的 ViewModel
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
这个 ViewModelStore 方法也很简单,只有一个 HashMap,而这个 Map 以键值对形式存放 ViewModel ,每次添加一个新的 ViewModel 都会检查当前Map中有没有同 Key 的 ViewModel,有则替换,并调用旧的ViewModel 的 onClear 方法
这里也有一个 clear 方法,方法中会遍历当前里面的所有 ViewMdel,依次调用 ViewModel 的 clear 方法,最后把当前的 HashMap 清空
所以, ViewModelStore 就是一个 ViewModel 的仓库,通过 HashMap 存放多个 ViewModel,提供了一个 clear 方法用于销毁全部的 ViewModel,释放内存
这里也就知道了,ViewModel 在一般情况下,是无法跨 Activity 共享数据的
再回到刚才的 ViewModel 创建方法:
MainViewModel model = new ViewModelProvider(this).get(MainViewModel.class);
这里使用 ViewModelProvider(this).get(MainViewModel.class) 来获取 MainViewModel 实例,那么来看下 get() 方法:
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (<