写在前面
组件架构三剑客终于来到了最后一篇:ViewModel,关于Lifecycle和LiveData可以看之前的文章。ViewModel和Lifecycle和LiveData的关联并不大,可以单独拿出来使用。这里用的依赖主要是AndroidX里面的,其他版本可能有些不同,但核心逻辑应该还是一致的。
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0-alpha01'
我们都知道,ViewModel的核心功能之一就是帮我们保存数据,特别是当Activity重建时还能将数据保存下来。页面重建保存数据,看到这里我们可能很容易的就想到了onSaveInstanceState()
方法,我们可以在这里保存数据,以便Activity重新创建时在onCreate()
方法中接收到保存下来的数据。但它是有局限的,只能保存一些简单的数据或者一些经过序列化的数据,并且数据量还不能太大。ViewModel的出现,正好弥补了这一不足。
ViewModel结构与Lifecycle的结构类似,Activity/Fragment除了实现一个LifecycleOwner接口外,还实现了一个ViewModelStoreOwner接口,它只有一个方法用来获取ViewModelStore,然后通过ViewModelStore来负责添加和移除ViewModel。
ViewModelProvider
ViewModel的创建是ViewModelProvider提供的。要看ViewModel是怎么被创建并添加进ViewModelStore的,还得先从我们最熟悉的api入手。
// 这里的this是指Activity
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
先创建一个ViewModelProvider,再通过它来获取ViewModel。
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
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(activity.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
this(store, new FactoryWrapper(factory));
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull KeyedFactory factory) {
// 要注意这里的mFactory是KeyedFactory
mFactory = factory;
mViewModelStore = store;
}
Factory
这个方法的目的是要构造一个ViewModelProvider,先来讲讲工厂Factory,ViewModelStore
放到后面讲。默认我们没有传工厂进来,这里会帮我们构建一个AndroidViewModelFactory对象。因为这里面出现了很多Factory相关的类,所以我觉得还是有必要先将Factory的结构讲一下,有助于了解。
结合上面的代码和类图来看,这个框架会默认给我们提供一个AndroidViewModelFactory工厂对象,然后又将它封装成了一个KeyedFactory对象,再加上ViewModelStore对象,一起构造出了ViewModelProvider。短短的一个方法里面出现了工厂模式(Factory)和装饰器模式(FactoryWrapper),真的是很佩服。
ViewModel
拿到ViewModelProvider对象后,再来看它的get方法:
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> 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);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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.
}
}
// mFactory指的是FactoryWrapper
viewModel = mFactory.create(key, modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
首先是根据class获取一个key值,然后先从mViewModelStore中查询有没有相应的ViewModel,当然我们第一次调用肯定是拿不到的,需要走下面的创建步骤,创建完再添加到ViewModelStore中去。
private static class FactoryWrapper implements KeyedFactory {
private final Factory mFactory;
FactoryWrapper(Factory factory) {
mFactory = factory;
}
@Override
public <T extends ViewModel> T create(String key, Class<T> modelClass) {
// mFactory指的是AndroidViewModelFactory
return mFactory.create(modelClass);
}
}
很奇怪的是这里的key并没有被用到,不知道后面会不会添加一些新的东西进来。
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (...) {
// 一些catch
}
}
return super.create(modelClass);
}
如果这个ViewModel是继承自AndroidViewModel的话,就用AndroidViewModelFactory创建一个,否则,用NewInstanceFactory创建一个普通的ViewModel。
Android给的建议是不要在ViewModel中使用到任何与Android相关的代码,最简单的检查办法就是看有没有import进*.android.*
相关的东西。但是这又有点不现实,因为我们经常需要用到Context去获取一些资源。为了解决这个问题,Android就给我们提供了AndroidViewModel。所以如果有这个需求的话可以让你的ViewModel继承AndroidViewModel。
另外,从ViewModel的创建过程来看,如果我们需要在构造函数里传一些别的参数的话,就需要自己去构建工厂类了。
讲完了Factory,再回过头来看ViewModelStore,Activity/Fragment创建ViewModel的过程唯一的区别就在于ViewModelStore获取方式的不同。
activity.getViewModelStore();
fragment.getViewModelStore();
Activity.getViewModelStore()
@NonNull
@Override
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;
}
首次调用这个方法,肯定是通过new方法拿到的,可以猜出ViewModelStore的缓存就是通过NonConfigurationInstances缓存下来的。而它又是在onRetainNonConfigurationInstance()
方法中保存下来的,然后使用getLastNonConfigurationInstance()
获取出来的。这个与onSaveInstanceState()
和onRestoreInstanceState()
是类似的。
@Override
@Nullable
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;
}
关于onRetainNonConfigurationInstance()
和getLastNonConfigurationInstance()
这两个方法可以来Activity中看一下具体的流程。首先状态得保存下来,才能在重建的时候取出来。既然是要保存状态,那肯定是在onDestroy()
的时候保存的,所以直接来看看ActivityThread$performDestroyActivity()
// ActivityThread.java
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
// 如果还没调用onPause()的话,调用onPause()
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
// 调用onStop()
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
try {
// 划重点,保存状态
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
try {
r.activity.mCalled = false;
// 调用onDestroy()
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
}
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to destroy activity " + safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
}
}
r.setState(ON_DESTROY);
}
mActivities.remove(token);
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
再继续跟到Activity里面
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
第一行就是调用onRetainNonConfigurationInstance()
来保存Activity的状态。这个方法返回的NonConfigurationInstances对象保存在ActivityClientRecord中,然后在重启Activity的onAttach()
方法中将NonConfigurationInstances拿回来,从而实现了数据的不丢失。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
......
// 取回NonConfigurationInstances
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
Fragment.getViewModelStore()
// Fragment.java
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
// FragmentManagerImpl.java
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
// FragmentManagerViewModel.java
@NonNull
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来保存ViewModelStore的,key是相应的Fragment的ID。哦对了,FragmentManagerViewModel自己也是一个ViewModel。
Fragment的数据恢复是在FragmentActivity$onCreate()
里面做的。
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 在这里恢复数据
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
......
}
最终走到FragmentManagerImpl中去:
public void attachController(@NonNull FragmentHostCallback host,
@NonNull FragmentContainer container, @Nullable Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
mHost = host;
mContainer = container;
mParent = parent;
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
// 这里是通过Activity的getViewModelStore()获取ViewModelStore的
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
在这里初始化mNonConfig
,所以我们才能通过mNonConfig.getViewModelStore()
去获取ViewModelStore。重新创建Activity的时候,是通过Activity的getViewModelStore()
去拿到ViewModelStore的,再通过它去拿到FragmentManagerViewModel。这里感觉有点绕。可以这么说吧,Fragment的ViewModelStore是存放到FragmentManagerViewModel中的,然后FragmentManagerViewModel又被放到了Activity的ViewModelStore中,Activity在保存数据的时候自然也就将FragmentManagerViewModel保存了下来,从而将Fragment的ViewModelStore保存了下来。
另外,网上有一些文章说Fragment的ViewModelStore是通过Fragment$setRetainInstance()
来保存的,可能是引用的版本号不一样吧,毕竟Android还在继续更新中。我这里通过调试发现是以这种方式保存的。可能以后版本不一样又会有所改动了。
最后
到这里,Lifecycle, LiveData和ViewModel的原理,总算是解析完了。其中至少是用到了观察者模式,工厂模式,装饰器模式,自己分析完之后还是有所收获的。也可以看到ViewModel的原理和Activity的生命周期最加紧密,先给自己挖个坑,后面找时间再来写一篇Activity的启动流程的。