目录
ViewModel有两个显著的优点:
- 同一个Activity下的不同Fragment之间可以用ViewModel通信;
- ViewModel的生命周期极长,即使其LifecycleOwer销毁了ViewModel也依旧存活;
第一个优点得益于ViewModel的组织架构,Activity和Fragment会缓存一个ViewModel集合,每一个ViewMode都是单例,以其Class名为key,以ViewMode实例为value存储在一个HashMap中。ViewModelStore是用于存储ViewModel的,Factory是用于生产ViewModel的,ComponentActivity中有一个ViewModelStore对象和一个Factory。每次要获取一个ViewModel时,就将ComponentActivity中的ViewModelStore和Factory对象组装为一个ViewModelProvider对象,该对象会对ViewModel进行管理和维护。
1. ViewModel的存取设计
1.1. ViewModelStore的提供
ViewModel模块中定义了ViewModelStoreOwner接口来表示ViewModelStore拥有者,该拥有者自然应该定义获取ViewModelStore的方法getViewModelStore()。ComponentActivity和Fragment都实现了该接口,ComponentActivity中的getViewModelStore()相对简单,即是会返回一个ViewModelStore单例。而Fragment中的实现相对复杂,是交给FragmentManager类的类型为FragmentManagerViewModel的成员变量mNonConfig来处理的,FragmentManagerViewModel继承与ViewModel。
//FragmentManagerViewModel.java
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
FragmentManagerViewModel的mViewModelStores成员变量是以Fragment.mWho为key,ViewModelStore对象为value的HashMap。FragmentManager管理许多个Fragment和每个Fragment对应的ViewModelStore,FragmentManager将这些信息存储在FragmentManagerViewModel中的mRetainedFragments和mViewModelStores中。
1.2. Factory的提供
获取一个ViewModel时,我们往往不需要自己传一个Factory对象,往往ViewModelStoreOwner都实现了默认的Factory提供方法。ViewModel模块中定义了HasDefaultViewModelProviderFactory接口来标示一个ViewModelStoreOwner有默认的Factory提供方案。ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口并且实现的Factory提供方法是基本一致的,都是返回一个SavedStateViewModelFactory对象,SavedStateViewModelFactory具有状态保存功能,通过SavedStateHandle实现。ViewModelProvider中有三类Factory:
- 虚类KeyedFactory,其creat()方法中有一个String类型的key参数,SavedStateViewModelFactory是其实现类
- NewInstanceFactory,采用零参构造器反射生成ViewModel对象
- AndroidViewModelFactory,采用零参构造器反射生成AndroidViewModel对象
1.3 ViewModel的存取
获取一个ViewModel是通过调用ViewModelProvider中的get()方法,其逻辑是先从ViewModelStore中获取缓存的ViewModel,若没有缓存着的ViewModel,则采用Factory生产一个ViewModel,看一下ComponentActivity和Fragement默认提供的SavedStateViewModelFactory类的create()方法的实现:
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
if (constructor == null) {
return mFactory.create(modelClass);
}
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if (isAndroidViewModel) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
}
主要逻辑是通过指定的构造器参数查找合适的构造器,并反射生成相应的ViewModel对象或AndroidViewModel对象。指定的构造区参数定义为:
private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,
SavedStateHandle.class};
private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};
从这里可知当ViewMode有定义含SavedStateHandle类型的参数的构造器中,则该ViewModel具有保存状态功能。如果没有找到指定参数的构造器,则会采用mFactory来生产ViewModel,mFactory的类型是AndroidViewModelFactory,AndroidViewModelFactory继承于NewInstanceFactory,AndroidViewModelFactory的creat()的逻辑是先试着创建一个AndroidViewModel对象,若创建不成功则采用父类的creat()方法创建一个ViewModel对象。
2. SavedStateHandle保存数据原理
要使用SavedStateHandle,需要引入下面的依赖:
androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion
通过上一节可以知道在通过反射构造器初始化一个ViewModel实例时,会传入一个SavedStateHandle对象,这个对象是调用SavedStateHandleController的getHandle()方法获取的,SavedStateHandle内部通过类型为HashMap的mRegular来保存变量,与SavedStateHandle相关的类的关系图如下所示:
简述这几个类的作用:
- SavedStateHandle用于保存变量,存储在HashMap类型的mRegular中;
- SavedStateHandleCntroller用于管理SavedStateHandle;
- SavedStateRegistry:用于消费和生产保存变量数据,SavedStateHandle可以向SavedStateRegistry注册保存变量提供者对象SavedStateProvider;
- SavedStateRegistryController:用于管理SavedStateRegistry;
- SavedStateProvider:保存状态提供者,用于将SavedStateHandle的保存状态提供给SavedStateRegistry,并序列化保存到Bundle中;
2.1. 状态保存过程
下面看一下保存数据过程。假设横竖屏切换,这时会调用ComponentActivity的onSaveInstanceState(),接着会调用SavedStateRegistryController的performSave()方法,最终会调用到SavedStateRegistry的performSave()方法:
void performSave(@NonNull Bundle outBundle) {
Bundle components = new Bundle();
if (mRestoredState != null) {
components.putAll(mRestoredState);
}
for (Iterator<Map.Entry<String, SavedStateProvider>> it =
mComponents.iteratorWithAdditions(); it.hasNext(); ) {
Map.Entry<String, SavedStateProvider> entry1 = it.next();
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
}
mComponents是以map形式保存注册进来的SavedStateProvider对象的,调用SavedStateProvider的saveState()方法即可将SavedStateHandle中的数据保存在Bundle对象中返回,最后保存在key=SAVED_COMPONENTS_KEY的Bundle中。
2.2. 状态恢复过程
当调用ComponentActivity的onCreate()方法时,最终会调用到SavedStateRegistry的performRestore()方法:
//SavedStateRegistry.java
void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
if (mRestored) {
throw new IllegalStateException("SavedStateRegistry was already restored.");
}
if (savedState != null) {
mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
}
mRestored = true;
}
从外面传入的Bundle中获取key=SAVED_COMPONENTS_KEY的类型为Bundle的对象并保存在mRestoredState成员变量中。当创建一个SavedStateViewModelFactory对象时会调用SavedStateHandleController的create()方法:
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
String key, Bundle defaultArgs) {
//从SavedStateRegistry对象中获取恢复状态信息
Bundle restoredState = registry.consumeRestoredStateForKey(key);
//以恢复的restoredState信息重新创建一个SavedStateHandle对象,虽然是不同的SavedStateHandle对象,但是其内部数据应该和以前一样
SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
SavedStateHandleController controller = new SavedStateHandleController(key, handle);
controller.attachToLifecycle(registry, lifecycle);
tryToAddRecreator(registry, lifecycle);
return controller;
}
又回到SavedStateRegistry中,通过consumeRestoredStateForKey()方法返回key对应的restoredState值,看一下这个方法的实现:
public Bundle consumeRestoredStateForKey(@NonNull String key) {
if (!mRestored) {
throw new IllegalStateException("You can consumeRestoredStateForKey "
+ "only after super.onCreate of corresponding component");
}
if (mRestoredState != null) {
Bundle result = mRestoredState.getBundle(key);
mRestoredState.remove(key);
if (mRestoredState.isEmpty()) {
mRestoredState = null;
}
return result;
}
return null;
}
这里的mRestoredState即是在performRestore()方法中恢复并保存的值。
3. ViewModel的超长生命周期原理
当配置发生变化时(横竖屏切换),Activiry会先Destory然后会创建一个新的Activity实例,然而新Activity中的ViewModelStore实例却没有重新创建,因此ViewModel也不会重新创建了,引用官方的图可以清晰展示:
这与Activity的onSaveInstance()/onRestoreInstance()的原理是一致的。下面看一下横竖屏切换的部分时序图:
这里Activity的lastNonConfigurationInstances可以缓存ViewModelStore的,在performDestroyActivity()方法中会缓存一些数据到ActivityClientRecord中:
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
}
r.setState(ON_DESTROY);
}
return r;
}
如果getNonConfigInstance为真则在Activity销毁时会缓存lastNonConfigurationInstances到ActivityClientRecord中,Activity中的retainNonConfigurationInstances()方法最终会调用到onRetainNonConfigurationInstance()方法:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
这里会将Activity中的viewModelStore对象缓存在一个在NonConfigurationInstances对象中的viewModelStore中。ComponentActivity中获取ViewModelStore是会先从lastNonConfigurationInstances来获取,若不成功,则创建一个新的ViewModelStore实例:
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
通过以上分析可以看出缓存实现的原理: 将要缓存的数据在Activity销毁的时候缓存在ActivityClientRecord中,当屏幕切换时虽然Activity会销毁,但是ActivityClientRecord不会销毁,因此缓存得以保存。