在上一篇博客中,我们从总体上介绍了AAC架构组件,以及Lifecycle组件的使用和源码解析,现在我们来了解ViewModel组件的使用和源码。
ViewModel是android架构组件中非常重要的一个组件,它是Android架构分层的核心,有关它的用法和资料可以可以参考官方给出的示例https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html 。
ViewModel的官方定义如下:ViewModel是存储和管理lifecycle 重新创建的数据的组件,在lifecycle 在配置改变或者屏幕旋转时数据仍然在生存周期内。另外ViewModel还可以用来负责UI组件间的通信,它是解耦Activity/Fragment View层的关键。
可以看到它位于Activity(View)和数据仓库(Model)层之间,类似于MVP中的P或者mvvm中的vm,那么问题就来了,大家都知道,我们平时使用mvp或者mvvm的过程中,Present或者ViewModel类都是我们自己定义的,并不需要继承自某个类,google为啥要在AAC中定义ViewModel这个类让我们继承呢?
这是因为,ViewModel组件的实例在config发生变化或者屏幕旋转时,不会被Activity/fragment重新创建,这个特性在我看来就是ViewModel组件最大以及唯一的意义,如果不是这个特性,我们完全可以用自己的实体类来替代google的ViewModel。或者反过来,我们也可以在学习了源码之后,在MVP中引入该机制,来让config变化时保存数据更容易。官方给出的ViewModel组件的生命周期图如下:
可以看到,在Activity onCreate之后,ViewModel对象就被创建,而只有在Activity的finish方法调用后,ViewModel的onCleared方法被回调,之后才会销毁ViewModel对象,这是如何做到的呢?我们之后详细分析。
首先我们需要创建一个类继承自ViewModel,然后就没有然后了,我们可以将该类当做普通的Present或者VM来做我们自己想要的业务逻辑,官方示例中一般使用LiveData来传递数据,但是其实ViewModel组件和我们之前介绍的Lifecycle和之后要介绍的LiveData之间其实都不存在关系,我们完全可以在ViewModel中使用Rxjava甚至其他非观察者的方式来实现业务逻辑,说到底,ViewModel组件只是给我们提供了一个系统配置变化后保存数据的地方而已。
public class MyViewmodel extends ViewModel{
//可以添加任意变量
private String str = "text";
//可以添加任意方法
public String getStr(){
return str;
}
}
其次,我们在Activity或者fragment中通过ViewModelProviders获取到我们的MyViewModel实例,就可以向普通Present一样使用它了。注意获取方式是像示例中这样固定的方式,自己创建MyViewModel的对象,是达不到让系统保持数据的效果的。
public class MainActivity extends AppCompatActivity implements LifecycleObserver{
@SuppressLint("RestrictedApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//固定套路获取我们自己的ViewModel
MyViewmodel vm = ViewModelProviders.of(this)
.get(MyViewmodel.class);
//直接向Present一样使用其中的方法即可
vm.getStr();
}
}
我们首先来看一些ViewModel组件的主要类图,可以看到,主要的类并不多。ViewModel是我们需要继承的抽象类,ViewModelStore用来缓存创建的ViewMdodel对象,ViewModelStores是工具类,主要用于产生ViewModelStore。
ViewModelProvider中包含了ViewModelStore的引用,主要用于向外部提供ViewModel对象,ViewModelProviders是工具类,主要用于产生ViewModelProvider对象。
我们先来看看ViewModel类,毕竟我们之后都需要继承自它来编写自己的业务逻辑,可以看到,它是一个抽象类,内部只有一个空方法onCleared,在该类被销毁前调用。
public abstract class ViewModel {
//该方法会在Activity finish时被调用
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
该类是一个存储ViewModel的仓库,将ViewModel按照名称存储。我们可以方便的根据名称放入和取出ViewModel,我们来看一下代码:
public class ViewModelStore {
//按照名称存储ViewModel
private final HashMap mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get(String key) {
return mMap.get(key);
}
//调用每个ViewModel的onCleared回调方法,并清空map
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
可以看到,代码非常简单,其实就是用一个HashMap存储了名称和ViewModel的键值对,方便之后使用而已,没有什么好说的。
该类的作用为根据我们传入的class对象,创建ViewModel实例并返回给我们,我们来看一下代码。
public class ViewModelProvider {
public interface Factory {
//根据传入的class对象,创建其ViewModel实例
T create(Class modelClass);
}
//Factory的一个实现类,使用class的newInstance方法创建ViewModel实例
public static class NewInstanceFactory implements Factory {
@Override
public T create(Class modelClass) {
try {
return modelClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
//创建ViewModel实例的Factory对象
private final Factory mFactory;
//存储ViewModel实例的ViewModelStore对象
private final ViewModelStore mViewModelStore;
//构造方法
public ViewModelProvider(ViewModelStore store, Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
//根据Class对象,获取对应的ViewModel对象
public T get(Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
//根据名称,从ViewModelStore的缓存中获取ViewModel对象
ViewModel viewModel = mViewModelStore.get(key);
//如果获取成功了的话,直接返回。
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
}
//如果缓存中没有,则使用Factory类创建
viewModel = mFactory.create(modelClass);
//将ViewModel加入缓存
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
该类首先定义了一个Factory的接口,作用是根据传入的class产生实际的ViewModel对象,并且定义了一个该接口的默认实现类NewInstanceFactory,该类很简单,直接使用了class的newInstance方法构建一个对象,不多说了。
该类中有2个类成员变量,一个是Factory对象,用来创建ViewModel,一个是ViewModelStore,用来缓存ViewModel。我们来看看该类中最重要的get方法,就清楚了这2个成员变量的作用。
get方法的实现也比较简单,首先根据名称从ViewModelStore缓存中获取ViewModel,如果获取到的话,直接返回该ViewModel,如果缓存中没有的话,使用Factory对象新建一个ViewModel,并将其加入到ViewModelSote缓存中,就是一个很简单的缓存逻辑而已。
下面我们从ViewModel的使用示例中,来分析一下ViewModel组件的工作流程,还记得如果获得ViewModel对象么,很简单,在Activity中直接一行代码,轻松获取,见下面代码。
MyViewmodel vm = ViewModelProviders.of(this).get(MyViewmodel.class);
当然啦,看起来简单的代码,实际未必简单,我们下面详细分析。
我们来看一下of方法,首先创建一个单实例的DefaultFactory对象,该DefaultFactory对象是Factory接口的一个默认实现,该DefaultFactory针对AndroidViewModel类会使用带Application参数的构造器创建ViewModel对象,而对于普通ViewModel类,使用不带创建的构造器。然后使用ViewModelStore和DefaultFactory对象构建一个ViewModelProvider,并将其返回,这样就完成了of方法的工作,代码比较简单,并且已经对于较重要的步骤进行了注释,就不细说了。
public class ViewModelProviders{
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
//创建默认的Factory实例
initializeFactoryIfNeeded(activity.getApplication());
//使用ViewModelStore和Factory构建ViewModelProvider对象,并返回结果。
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
//创建单实例的默认DefaultFactory对象
private static void initializeFactoryIfNeeded(Application application) {
//类似于单例模式,sDefaultFactory为空则创建
if (sDefaultFactory == null) {
sDefaultFactory = new DefaultFactory(application);
}
}
//存储DefaultFactory的静态实例
private static DefaultFactory sDefaultFactory;
//默认Factory类,继承了我们之前说过的NewInstanceFactory
public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory {
//保存Application对象
private Application mApplication;
public DefaultFactory(@NonNull Application application) {
mApplication = application;
}
@Override
public T create(Class modelClass) {
//如果ViewModel继承自AndroidViewModel类,则使用带Application参数的构造器创建对象
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (Exception e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
//否则使用不带参数构造器创建ViewModel对象
return super.create(modelClass);
}
}
上面我们分析ViewModelProviders的of方法在创建ViewModelProvider的实例时,调用了ViewModelStores类的of方法来获取一个ViewModelStore实例,我们就来分析一下它的of方法。
@MainThread
public static ViewModelStore of(FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
可以看到,代码很简单,就一句话搞定,我们看一下holderFragmentFor的实现,产生了一个HolderFragment,那是什么东东呢不明觉厉啊,我们赶紧跟过去看看再说。
HolderFragment类是一个Fragment,Fragment是大家平时常用的组件,只不过我们平时一般都是用它来展示ui,解耦Activity。而ViewModel这里给我们展示了Fragment的另一种用法,即完全没有ui界面,而是作为一个数据容器使用,那么这个数据容器装载的是什么呢?答案就是我们之前说的ViewModelStore。
对于每一个使用了ViewModel的Activity/Fragment,我们都将向它添加一个没有界面的HolderFragment,HolderFragment中包含一个ViewModelStore,其中按照名称存储了所有的VIewModel。也就是说我们使用的ViewModel都是从HolderFragment中拿出来的。
我们已经知道,我们最终使用的ViewModel保存在HolderFragment中,那么要想配置变化时,我们的ViewModel不被销毁,必须保证HolderFragment不会被销毁,这要如何做到呢?其实fragment中已经有这样的方法了,那就是setRetainInstance(true)方法,我们来看一下该方法的源码。
/**
* 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;
}
该方法的实现虽然只有一句话,不过作用确很重要,我们看注释可以知道,如果该标志被设置为true,那么当Activity因为配置变化而重建时,该fragment的实例将会被保留,并且生命周期的onDestroy方法不会被调用。
上面我们已经从整体上说明了ViewModel为什么可以在配置变化时,保存数据的原理,下面我们来详细看看HolderFragment的源码,分析一下该过程具体的实现。
public class HolderFragment extends Fragment {
//HolderFragment管理器对象,管理了所有HolderFragment的添加,获取等。
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String HOLDER_TAG =
"android.arch.lifecycle.state.StateProviderHolderFragment";
//ViewModelStore对象,用来存储ViewModel
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
//构建时,调用该方法,保证HolderFragment在配置变化导致Activity重建时,其实例不会被销毁重建
setRetainInstance(true);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通知HolderFragment管理器,自身已经创建成功了
sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onDestroy() {
super.onDestroy();
//HolderFragment销毁时,ViewModelStore中存储的ViewModel才会被销毁
mViewModelStore.clear();
}
//返回我们存储的ViewModelStore对象
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
//静态方法,返回HolderFragment对象
return sHolderFragmentManager.holderFragmentFor(activity);
}
@SuppressWarnings("WeakerAccess")
static class HolderFragmentManager {
//该map存储了已经执行了Commit但是尚未添加到宿主Activity的HolderFragment,防止HolderFragment
//重复添加。
private Map mNotCommittedActivityHolders = new HashMap<>();
//Activity生命周期回调
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
//如果Activity在HolderFragment尚未添加成功时被销毁,
//则我们将它从尚未成功commit的map中移除
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
}
};
//标志位,判断Application是否添加了生命周期监听器
private boolean mActivityCallbacksIsAdded = false;
//HolderFragment已经成功添加到宿主Activity了,我们将其从尚未成功map中移除
void holderFragmentCreated(Fragment holderFragment) {
Fragment parentFragment = holderFragment.getParentFragment();
//省略部分内容
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
//根据HOLDER_TAG,从宿主Activity,fragment中查找对应的HolderFragment
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;
}
//创建HolderFragment并使用HOLDER_TAG将其添加到宿主Activity,fragment中
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
//从宿主Activity中获取HolderFragment,如果没有则添加
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
//根据HOLDER_TAG标签,查找HolderFragment是否已经存在
HolderFragment holder = findHolderFragment(fm);
//如果存在直接返回。
if (holder != null) {
return holder;
}
//从已经Commit但是尚未成功添加到宿主Activity的map集合中查找该fragment
holder = mNotCommittedActivityHolders.get(activity);
//如果找到,则直接返回
if (holder != null) {
return holder;
}
//如果全局的Activity生命周期监听器尚未添加,则我们添加
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
//创建HolderFragment,并将其加入map中
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
}
}
首先我们来看获取HolderFragment的holderFragmentFor方法,该方法为静态方法,外部调用它来获取HolderFragment的实例,该方法直接调用了内部类HolderFragmentManager的同名方法,该内部类主要对所有的HolderFragment进行管理。
我们继续看HolderFragmentManager的holderFragmentFor方法,可以看到,其执行流程如下:
1,使用一个HOLDER_TAG,从FragmentManager中查找HolderFragment是否已经添加了,如果找到,直接返回。
2,从mNotCommittedActivityHolders这个map中查找HolderFragment,如果找到,直接返回。
3,如果Application中没有添加Activity生命周期监听,添加生命周期间监听器。
4,使用HOLDER_TAG这个TAG,创建一个HolderFragment,并添加到宿主Activity中。
5,将该HolderFragment加入mNotCommittedActivityHolders这个map,并返回HolderFragment。
通过这个方法,我们就完成了将HolderFragment添加到宿主Activity中,并且可以随时获取的功能,不过这里有一点疑问,那就是我们为何需要mNotCommittedActivityHolders这样一个key为Activity,值为HolderFragment的hashMap来保存HolderFragment呢?按道理,我们只要使用findFragmentByTag就可以找到我们添加的HolderFragment了啊。
原来我们将HolderFragment通过FragmentManager的commit方法添加了之后,HolderFragment并没有被立即添加到宿主Activity中,还需要系统执行一定的其他操作,而如果此时我们再执行HolderFragmentFor方法,这样对于同一个Activity就会创建多个HolderFragemnt实例,所以我们将它保存到map中,并且在HolderFragment真正onCreate的时候移除,这样就达到了一个宿主Activity只存在唯一的HolderFragment的目的。
该方法很简单,返回内部创建的ViewModelStore,由于HolderFragment在配置变化时不会销毁,所以它内部的ViewModelStore也不会销毁,因此我们存储在其中的ViewModel自然也不会销毁啦。
我们先看一下整体的流程图,大概分为如下几步:
1,ViewModelProviders工具类创建ViewModelProvider对象。
2,ViewModelStores工具类获取ViewModelStore对象。
3,创建HolderFragment,加入宿主Activity,并返回ViewModelStore对象。
4,ViewModelProvider使用Factory接口创建ViewModel对象,使用ViewModelStore缓存ViewModel对象。
最后再附上一张时序图,上面标明了我们分析的所有类,以及ViewModel的主要流程方法,相信大家看了这张图之后,再看代码就会更清晰啦。
1,onSaveInstance适合保存少量数据,而ViewModel则可以保存较多的数据。
2,onSaveInstance保存的数据保存在系统进程中,因此如果用户app因内存不足被系统杀死时,onSaveInstance保存的数据还可以拿回来。而ViewModel保存的数据则消失了。
3,2者都只适合保存临时性的数据,如果想要持久化保存,请使用database或者文件。
很多同学表示,自己习惯或者公司早就已经在使用比较成熟的mvp架构了,google的AAC虽然看起来不错,但是贸然切过去,成本和风险还是比较大的,感觉根本就没有机会使用啊。
这里其实我想说,AAC架构是独立的几个部分,哪怕我们不使用google推荐的mvvm架构,我们也还是可以自由的使用Lifecycle和ViewModel啊,比如我们可以这样使用,先创建一个Present,让它继承自ViewModel类并且实LifecycleObserver接口。
public class Present extends ViewModel implements LifecycleObserver{
public Present(){
}
public void test(){
Log.d("Present"," i am a test");
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate(){
Log.d("Present"," onCreate");
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy(){
Log.d("Present"," onDestroy");
}
}
在Activity的onCreate方法中这样使用。这样岂不就即ViewModel组件提供的配置变化时,我们的Present不会被销毁的功能,又实现了Lifecycle组件提供的观察生命周期变化的功能了么。AAC的2个组件一下就派上了用场,而且还是在我们常用的mvp模式中,并不强制需要切换到mvvm模式,岂不美哉。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Present pm = ViewModelProviders.of(this)
.get(Present.class);
getLifecycle().addObserver(pm);
pm.test();
}
当然啦,有同学可能要说,我们明明是mvp,但是需要继承自ViewModel,还要使用神马ViewModelProviders啦,不是很容易让人误解,感觉不明觉厉么。
那我只能说,少年,源码也有啦,分析也有啦,不想用现成的,直接自己造着源码撸一套不就行了么,反正也不复杂。
1,HolderFragment的使用,Fragment是我们平时常用的ui组件,我们一般用它来组织界面显示,达到复用的目的。而ViewModel向我们展示了Fragment的另外一种用法,即用来存储数据,而不是显示界面,作为一个数据容器使用。回想我们上篇讲过的Lifecycle,其ReportFragment也是不显示界面。我们发现,原来对fragment的理解还是太片面了。
2,Fragment的setRetainInstance方法,ViewModel组件虽然内容不少,但是核心还是该方法,让系统替我们保存fragment的实例,从而达到保存数据德目的。
3,使用一些静态工具类,例如ViewModelProviders,ViewModelStores来提供工厂方法创建实例,而不是直接提供构造方法创建实例,这样隐藏了创建对象的细节,便于抽象和管理。