最近面试被问到了 ViewModel 和 onSaveInstance 的原理,当下整个人是懵逼的,事后整理了一下,还真有不少的点
1. 使用场景
- onSaveInstance()
- ViewModel
2. 实现原理上的区别
2.1 ViewModel 的原理
基于:
onRetainCustomNonConfigurationInstance()- ViewModel 的保存getLastCustomNonConfigurationInstance()- (重建时)ViewModel的获取 两个方法,网上很多资源,不重复
3. 一些问题
3.1 同一个 Activity 上绑定的两个不同 Fragment 中持有的 ViewModel 对象是同一个吗?
答: 可以是,可以不是
因为: 首先要获取一个 viewModel 实例肯定是要通过 ViewModelProvider#get() 方法得到。
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 获取
ViewModel viewModel = mViewModelStore.get(key);
// 检查是否存在已创建实例
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
// 省略无关代码
}
// 创建
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
// 保存
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
get 方法主要逻辑就三个
- 首先会从 mViewModelStore 中尝试获取 viewModel 实例,获取到就直接返回
- 如果获取不到就会创建
- 创建后会放入 mViewModelStore 并返回实例
所以,如果 Activity 和 Fragment 上的 mViewModelStore 是同一个,那么取出来的 viewModel 一定是之前创建的。
又因为 mViewModelStore 是 ViewModelProvider 创建时候传入的
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
ViewModelProvider 一共有三个构造函数,无论是通过那个构建 ViewModelProvider,最终都会调用到最后一个上。 如果直接调用第三个,传入同一 ViewModelStore 实例,那最终获取到的 viewModel 实例必然也是同一个
继续回到剩余的两个构造函数,Activity 和 Fragment 自然就是传入的第一个参数 owner。 准确说这里的 Activity 和 Fragment 应该分别是 androidx 包下的 ComponentActivity 和 Fragment 的子类,因为他两为了支持 ViewModel 都实现了 ViewModelStoreOwner 接口, ViewModelStoreOwner 接口上又有 getViewModelStore() 方法用于获取 ViewModelProvider 实例。
Activity 上的比较简单,就不赘述了,主要看下 Fragment 上的, Fragment 将最终的实现代理给了 FragmentManger
public ViewModelStore getViewModelStore() {
// 省略无关代码
return mFragmentManager.getViewModelStore(this);
}
再看 FragmentManger#getViewModelStore
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
mNonConfig 的类型是 FragmentManagerViewModel,并且是在 Fragment attach时候赋值的
// FragmentManagerViewModel 中
ViewModelStore getViewModelStore(@NonNull Fragment f) {
// 别的不说就从 f.mWho 这里来看 最终获取到的 ViewModelStore 就是不同的,因为每个 fragment 的mWho 就是唯一的
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
public class Fragment {
String mWho = UUID.randomUUID().toString();
// 省略其他代码
}
另外,再从业务角度看,假设两个fragment 是放在 viewPager 中的,翻页时,两个 fragment 的生命周期也是不一致的,而 viewModel 又是通过 lifecycle 关联了组件的生命周期的,这样 onClear 方法的调用也是冲突的
3.2 ViewModel 和 onSaveInstance 的实现原理一样吗?
这一篇写的很不错:
ViewModel源码研究之聊聊onSaveInstanceState和onRetainNonConfigurationInstance的区别
补充:
- 此外,onSaveInstance() 是可以由参数控制是否写入到SP中,以进行数据持久化,参见这篇:Android onSaveInstanceState/onRestoreInstanceState 原来要这么理解
- 一些其他琐碎的点:刨根问底,为什么屏幕旋转后Activity重建了,但ViewModel不会重建。
3.3 Activity 重建前后,ViewModel的实例是同一个吗?
答: 是同一个,可以通打过打印 identityHashCode 得以验证。原理同上 3.2 ,因为 ViewModelStore 被保存在了同APP内的*ActivityClientRecord , 继而也可以得知的是:当应用被杀了之后,ViewModel是无法进行数据的恢复的。
PS: Activity 不是同一个对象