ViewModel 和 onSaveInstance

181 阅读3分钟

最近面试被问到了 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 方法主要逻辑就三个

  1. 首先会从 mViewModelStore 中尝试获取 viewModel 实例,获取到就直接返回
  2. 如果获取不到就会创建
  3. 创建后会放入 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的区别

补充:

3.3 Activity 重建前后,ViewModel的实例是同一个吗?

答: 是同一个,可以通打过打印 identityHashCode 得以验证。原理同上 3.2 ,因为 ViewModelStore 被保存在了同APP内*ActivityClientRecord , 继而也可以得知的是:当应用被杀了之后,ViewModel是无法进行数据的恢复的。 PS: Activity 不是同一个对象