ViewModel是Google官方MVVM架构的核心组件之一。同时,在Google推出的Android Jetpack组件中,也将ViewModel放在了Architecture类别之中。ViewModel类旨在以一种有生命周期的方式存储和管理与UI相关的数据,并且不会因为屏幕旋转等配置变化后而销毁。
从ViewModel生命周期图中可以看出我们发现,当activity因屏幕旋转而销毁,但是ViewModel一直存在,正是因为ViewModel有如此的生命周期,所以ViewModel在MVVM中可以作为数据存储区,是连接View和Model重要组件。
本文主要通过分析ViewModel源码,搞清楚如下几个问题:
- ViewModel是如何创建的?
- ViewModel是如何存储的,具体放在哪?
- ViewModel如何实现旋转屏幕而不销毁?
一、ViewModel创建过程
使用过ViewModel的同学都应该知道,当我们想要创建一个ViewModel对象的时候,不是通过new关键字去创建的,而是通过ViewModelProviders.of(this).get(ViewModel.class)这种调用方式创建的,简言之就是通过ViewModelProviders去创建。其实,我们想想也可以知道,ViewModel想要有生命周期,那么它势必就要和我们的Activity或者Fragment相关联起来,至于如何关联,源码会给我们答案。
我们先来看下ViewModelProviders.of()方法,调用这个方法最终会走到下面这2个方法其中之一,一个是在Activity里面调用,另一个则是在Fragment中调用:
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
我们看到这个方法最终会返回一个ViewModelProvider对象,在生成这个对象的时候需要传递2个参数,第一个参数是ViewModelStore,第二个参数是Factory。第一参数我们等到讨论ViewModel具体是存储在哪边的时候再说,暂且略过。这边先重点看下第二个参数Factory,从代码上来看,如何我们没有显示地传入Factory对象的话,就会去拿一个默认的Factory对象。事实也就是如此,而且这个默认的Factory对象还是个单例。其实,AndroidViewModelFactory就是ViewModelProvider的一个静态内部类,具体代码如下所示:
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public T create(@NonNull Class modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
至此,我们的ViewModelProvider对象总算是创建完成了,接下来就要调用它的get()方法来具体创建我们所需要的ViewModel对象了,具体如下:
// ViewModelProvider # get(@NonNull Class modelClass)
public T get(@NonNull Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
// ViewModelProvider # get(@NonNull String key, @NonNull Class modelClass)
public T get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
由上述代码我们可以知道,对于我们想要获取的ViewModel对象,首先会根据key从mViewModelStore中去寻找,看看是否已经创建过,如果找到则直接返回。如果之前没有创建过,则调用mFactory的create()方法去创建,如果我们没有显示地传入Factory对象,则调用默认的Factory对象(即AndroidViewModelFactory单例对象)去创建,由AndroidViewModelFactory源码可知它的create()方法会通过反射去创建我们的ViewModel对象。在创建完毕之后,就会把这个ViewModel对象存入mViewModelStore之中,那么很明显这个mViewModelStore内部肯定是一个HashMap结构。至此,我们已经将ViewModel创建过程解析完毕了,接下来看下创建好的ViewModel是存储在什么地方的。
二、ViewModel存储位置
在解析ViewModel创建过程时,我们遗漏了一个点,那就是创建ViewModelProvider对象时传入的第一个参数ViewModelStore,它是通过ViewModelStores.of(activity)或者ViewModelStores.of(fragment)来创建的。其实,创建出来的ViewModelStore对象最后复制给了我们上面所提到的mViewModelStore,具体可以看下如下源码:
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
而我们通过分析ViewModel创建过程,知道了最终创建出来的ViewModel对象,就是存储在mViewModelStore之中的。那么,现在就很明显了,ViewModel的存储位置其实就是mViewModelStore的存储位置,我们需要搞清楚ViewModelStores.of()方法的执行过程就行。
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
我们具体看下ViewModelStores.of()方法的执行过程,由于我们的Activity或者Fragment一般不会去实现ViewModelStoreOwner接口,所以基本都是通过holderFragmentFor(xxx).getViewModelStore()这种方式去创建ViewModelStore对象。
那么,我们先来看下这个创建方式的前半段holderFragmentFor(xxx):
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
很明显,这个方法返回的是一个HolderFragment对象,而且它继承自Fragment。HolderFragment对象的创建都是通过调用sHolderFragmentManager.holderFragmentFor()方法实现的,那么这个sHolderFragmentManager其实是HolderFragment内部类HolderFragmentManager的对象,我们一起看下它的holderFragmentFor()方法,这里以入参为Activity为例:
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
private static HolderFragment findHolderFragment(FragmentManager manager) {
if (manager.isDestroyed()) {
throw new IllegalStateException("Can't access ViewModels from onDestroy");
}
Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
}
}
};
由源码可知,会先去查找Tag为HOLDER_TAG的HolderFragment是否已经创建过,如果已经创建过则直接返回,从这我们可以得到的信息是一个Activity/Fragment只会创建一个HolderFragment。如果没有找到,则会从mNotCommittedActivityHolders缓存中进行查找,同样如果找到就直接返回。如果仍然没有找到,则说明之前未创建过,则调用createHolderFragment()方法创建HolderFragment对象,并在完成创建之后存入mNotCommittedActivityHolders。同时,我们还注意到,这里还注册了Activity生命周期监听,目的是在Activity销毁的时候可以移除mNotCommittedActivityHolders中的对应数据。
之后,在拿到HolderFragment对象之后,通过调用它的getViewModelStore()方法就可以拿到ViewModelStore对象了,它其实就是HolderFragment的成员变量。
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
至此,通过以上分析,我们知道通过ViewModelStores.of()方法获取的ViewModelStore对象其实就是HolderFragment的成员变量。那么,也就是说我们创建的ViewModel对象其实也就是保存在所创建出来无UI的HolderFragment之中的。需要注意的一点是,对于同一个Activity/Fragment而言,这种无UI的HolderFragment只会存在一个,但是存在HolderFragment之中的ViewModel可以有多个,即一个Activity/Fragment可以对应多个ViewModel对象。
三、ViewModel旋转屏幕不销毁原理
ViewModel旋转屏幕不销毁的原理其实就在HolderFragment的构造方法之中。
public HolderFragment() {
setRetainInstance(true);
}
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
*
* - {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
*
- {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
*
- {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will
* still be called.
*
*/
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
}
由源码可知,setRetainInstance(boolean) 是Fragment中的一个方法。根据官方注释,我们可以知道,当设置setRetainInstance(true)时,当我们旋转屏幕的时候,Activity会重绘,但是Fragment不会重绘,它将会保留,这也就意味着旋转屏幕时Fragment的onCreate和onDestory方法都不会调用。
无UI的HolderFragment正是运用了这个特性,在其中存放一个专门用于存储ViewModel对象的ViewModelStore(其内部为HashMap),如此ViewModelStore中所有的ViewModel都不会因为屏幕旋转等配置变化后而销毁。
总结
在创建ViewModel对象的过程中,会生成一个无UI的HolderFragment。对于同一个Activity/Fragment,只会有一个HolderFragment,但是可以有多个ViewModel。ViewModel存储在无UI的HolderFragment中,具体以键值对形式存储在ViewModelStore的HashMap中。Activity/Fragment的HolderFragment会保存在全局单例的HolderFragmentManager的HashMap中,在Activity/Fragment销毁时会进行相应的移除。
链接:https://www.jianshu.com/p/538b82fda4a6