Android架构组件三 Android Architecture Components ViewModel组件解析
2018-02-10 18:07
956 查看
1 前言
ViewModel是android架构组件中非常重要的一个组件,它是Android架构分层的核心,有关它的用法和资料可以参考Android架构组件一 Android Architecture Components开发入门
也可以参考官方给出的示例https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
接下来我们从以下几个方面介绍ViewModel组件
2 ViewModel的作用
ViewModel组件的官方定义如下:The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
翻译过来就是ViewModel是存储和管理lifecycle 重新创建的数据的组件,在lifecycle 在配置改变或者屏幕旋转时数据仍然在生存周期内。另外ViewModel还可以用来负责UI组件间的通信,它是解耦Activity/Fragment View层的关键。
ViewModel在Android架构组件中的位置如下:
可以看到ViewModel处于Activity(View)和Repository(Model)层之间,它类似于MVP中的P层,向下对View层提供操作Model层的接口,向上使用观察者模式观察Model层,当Model层感兴趣的数据发生变化时通知View层,当然这个是ViewModel中引用的LiveData组件来实现的,这个将会在下面一篇博文中来讲解。
ViewModel组件有非常大的一个意义就是它的实例不会因为Activity/Fragment因为配置发生改变或者屏幕旋转时重新创建ViewModel对象。这个是ViewModel区别与MVP中P的基本特点之一。官方给出了如下的图示说明ViewModel的生命周期。
可以看到,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。关于这一点是如何做到的,参考下面的讲解。
注意:*在ViewModel中不要持有Activity等View层的引用,如果需要引用Context,请使用AndroidViewModel,它引用了Application Context*
官方说明如下:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context
3 ViewModel UML类图
查看Android架构组件的源码,我们可以得到如下的UML类图。这里有几个比较重要的类
ViewModelProviders:创建ViewModelProvider的工具类,提供以下四个方法创建ViewModelProvider
public static ViewModelProvider of(@NonNull Fragment fragment) public static ViewModelProvider of(@NonNull FragmentActivity activity) public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) public static ViewModelProvider of(@NonNull FragmentActivity activity,@NonNull Factory factory)
创建时可以指定创建的Factory ,默认已经有一个Factory,定义如下:
public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory { private Application mApplication; /** * Creates a {@code DefaultFactory} * * @param application an application to pass in {@link AndroidViewModel} */ public DefaultFactory(@NonNull Application application) { mApplication = application; } @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 (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); } }
重写了public < T extends ViewModel > T create(@NonNull Class < T > modelClass)创建ViewModel对象。
ViewModelProvider: 真正创建和存储ViewModel的类,将创建的ViewModel保存在ViewModelStore 对象mViewModelStore中
创建逻辑如下:
@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. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
ViewModelStores:创建ViewModelStore的工具类,关联着HoldFragment ,并返回HolderFragment中的ViewModelStore对象
ViewModelStore: 保存和管理ViewModel的类,里面引用一个HashMap来保存ViewModel对象。并在HolderFragment销毁时清理掉所保存的ViewModel对象。
public class ViewModelStore { private final HashMap<String, ViewModel> 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); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } }
HolderFragment:继承自Fragment,是ViewModel在配置改变不被销毁的秘密所在,它主要为ViewModelProvider创建ViewModel提供所需要的ViewModelStore。
在ViewModelStores中有如下关键代码:
public static ViewModelStore of(@NonNull FragmentActivity activity) { return holderFragmentFor(activity).getViewModelStore(); }
这里的holderFragmentFor就是HolderFragment中的方法,定义如下:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.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; }
有关这些类的详细实现请参考源码。
4 ViewModel解析
1 ViewModel实例的创建过程ViewModel实例的创建过程比较复杂,先经过ViewModelProviders类,再经过ViewModelStores,在跳转到HolderFragment得到ViewModelStore对象,最终跳转到ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> modelClass)方法中创建的。整个流程如下:
因此,这里我们最终来看ViewModelProvider的public < T extends ViewModel > T get(@NonNull String key, @NonNull Class< T> 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. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
主要做了以下几件事情
(1) 从mViewModelStore中获取ViewModel对象,mViewModelStore是一个ViewModelStore对象,在创建ViewModelProvider传递过来的,第一次肯定获取为NULL。
(2) 判断是否是ViewModel的实例对象,如果是直接返回,当获取的不为NULL时,且传递的Class对象正确,就就可以在这里直接返回了,说明VikewModel对象之前创立过
(3) 调用mFactory的< T extends ViewModel > T create(@NonNull Class< T> modelClass)创建一个ViewModel对象,mFactory由创建ViewModelProvider指定,一般通过反射创建该对象。例如默认的DefaultFactory对象。
(4) 创建好ViewModel对象后将该对象缓存起来,保存到mViewModelStore中并返回ViewModel对象。
这样,整个ViewModel创建的过程就结束了。还是借用一下别人的序列图。
2 ViewModel实例为什么不会因为Activity 重新创建而销毁?
这个问题很有意思,从一开始接触到ViewModel官方的说明我就很好奇,这个是如何实现的呢?我们知道,在Activity/Fragment 因为屏幕旋转,配置改变时,里面所引用的对象都会被重建,典型的是对话框,为此我们不得不在Activity的onSaveInstanceState()保存一些数据,然后在onCreate()中恢复数据。然而ViewModel却不需要,那么ViewModel是如何做到的呢?我们查看ViewModelProviders的源码发现每次的of()方法又都是创建一个新的ViewModelProvider对象的,这就更加疑惑了。
@MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { initializeFactoryIfNeeded(checkApplication(activity)); return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory); }
答案是有两个关键点:
(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期
(2) ViewModelProvider 中 mViewModelStore对象
这里分别依次来说明这两个关键点
(1) HolderFragment在Activity/Fragment 被 re-create 的生命周期
仔细阅读HolderFragment的源码,我们发现在构造方法中有这么一句
public HolderFragment() { setRetainInstance(true); }
很不起眼,却非常重要。
setRetainInstance(true);表明Fragment 不会因为Activity/Fragment 因为配置改变,屏幕旋转被销毁时,他会驻留在系统中,知道Activity/Fragment 被重新创建,然后onAttach上继续使用,也就是说HolderFragment在Activity/Fragment 因为配置改变,屏幕旋转被销毁时,不会走onDestory() –> onCreate()方法。这样保证了Activity/Fragment重新创建时还是之前那个HolderFragment对象,当然了里面的mViewModelStore也不会变。
(2) ViewModelProvider 中 mViewModelStore对象
由前面的分析得到Activity/Fragment重新创建时HolderFragment不会被重新创建,那么自然ViewModelStores中返回的ViewModelStore还是之前的ViewModelStore
@MainThread public static ViewModelStore of(@NonNull FragmentActivity activity) { return holderFragmentFor(activity).getViewModelStore(); }
这样子导致在创建ViewModelProvider传进去的ViewModelStore 对象还是Activity/Fragment之前的ViewModelStore 对象
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this.mViewModelStore = store; }
这样即使new 再多的ViewModelProvider实例,它里面的mViewModelStore 对象并没有被重新创建。这样在调用get(@NonNull String key, @NonNull Class < T> modelClass)方法时,将会在步骤(2)中返回,返回的ViewModel对象就是Activity/Fragment重新创建之前的那个ViewModel对象。
通过(1)(2)就保证了ViewModel实例不会因为Activity 重新创建而销毁。
3 HoldFragment解析
在ViewModel组件中定义了一个HoldFragment类,该类继承Fragment,但是里面没有任何布局文件,它最主要的作用就是来生成在Activity/Fragment重新创建时保证返回的ViewModelStore对象不会改变。为此它的实现如下:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public class HolderFragment extends Fragment { private static final String LOG_TAG = "ViewModelStores"; private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager(); /** * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static final String HOLDER_TAG = "android.arch.lifecycle.state.StateProviderHolderFragment"; private ViewModelStore mViewModelStore = new ViewModelStore(); public HolderFragment() { setRetainInstance(true); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); sHolderFragmentManager.holderFragmentCreated(this); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override public void onDestroy() { super.onDestroy(); mViewModelStore.clear(); } public ViewModelStore getViewModelStore() { return mViewModelStore; } /** * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); } /** * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(Fragment fragment) { return sHolderFragmentManager.holderFragmentFor(fragment); } @SuppressWarnings("WeakerAccess") static class HolderFragmentManager { private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>(); private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>(); 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); } } }; private boolean mActivityCallbacksIsAdded = false; private FragmentLifecycleCallbacks mParentDestroyedCallback = new FragmentLifecycleCallbacks() { @Override public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) { super.onFragmentDestroyed(fm, parentFragment); HolderFragment fragment = mNotCommittedFragmentHolders.remove( parentFragment); if (fragment != null) { Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment); } } }; void holderFragmentCreated(Fragment holderFragment) { Fragment parentFragment = holderFragment.getParentFragment(); if (parentFragment != null) { mNotCommittedFragmentHolders.remove(parentFragment); parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks( mParentDestroyedCallback); } else { mNotCommittedActivityHolders.remove(holderFragment.getActivity()); } } 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; } 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; } HolderFragment holderFragmentFor(Fragment parentFragment) { FragmentManager fm = parentFragment.getChildFragmentManager(); HolderFragment holder = findHolderFragment(fm); if (holder != null) { return holder; } holder = mNotCommittedFragmentHolders.get(parentFragment); if (holder != null) { return holder; } parentFragment.getFragmentManager() .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false); holder = createHolderFragment(fm); mNotCommittedFragmentHolders.put(parentFragment, holder); return holder; } } }
首先在构造方法中调用了setRetainInstance(true),这样子在Activity/Fragment 因为配置发生改变时重新创建时,就不会销毁该HolderFragment 的实例,这样子HolderFragment 在下次Activity/Fragment重新创建时,并不会回调onDestory(),onCreate()方法,关于这一点。可以打点确认。
HoldFragment将创建实例的方法交给了HolderFragmentManager这个内部类,这个内部类首先创建了两个Map来保存了还未Attach但将要Attach 到Activity/Fragment的HoldFragment实例,并且注册了Activity/Fragment 的生命周期回调。接着创建HolderFragment实例,然后添加到map中去。
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; }
最后在HolderFragment的onCreate() 移除Map中添加的HoldFragment实例。因为回调到onCreate()方法,就说明HoldFragment已经成功的Attach到相应的Activity/Fragment 上了,这个时候需要移除mNotCommittedActivityHolders中的HoldFragment实例。
最后,在Activity finish之后,HoldFragment 会调用onDestory()方法,在这里面会清理掉mViewModelStore 中的ViewModel。
至此,ViewModel组件的分析已经基本完毕,下一篇将分析Android架构组件中LiveData组件
参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
https://shymanzhu.com/2017/12/28/Android%E6%9E%B6%E6%9E%84%E7%BB%84%E4%BB%B6%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94ViewModel/
相关文章推荐
- Android架构组件四 Android Architecture Components LiveData组件解析
- Android架构组件二 Android Architecture Components Lifecycles 组件解析
- Android Architecture Components应用架构组件源码详解(基于1.0以上)(第一篇生命周期监听分离LifecycleObserver和LifecycleOwner源码详解)
- 改造 Android 官方架构组件 ViewModel
- [置顶] Android架构组件ViewModel和LiveData介绍及使用
- Android架构组件之ViewModel
- [译]Android架构组件 – 查看ViewModel – 第二部分
- Android官方架构组件介绍之ViewModel
- Android Acrchitecture Components( 架构组件)+热门框架(Retrofit+OkHttp+RxJava2+Glide)
- Android Architecture Components应用架构组件源码详解(基于1.0以上)(第二篇ViewModel和LiveData)
- Android开发之WebView组件的使用解析
- Android UI 设计之 TextView EditText 组件属性方法最详细解析
- 深入解析Android中的RecyclerView组件
- 【Android 应用开发】Android UI 设计之 TextView EditText 组件属性方法最详细解析
- Android开发之WebView组件的使用解析
- 架构组件之 ViewModel | 中文教学视频
- 【Android 应用开发】Android UI 设计之 TextView EditText 组件属性方法最详细解析
- android中SurfaceView组件使用解析
- 【框架篇】mvc、mvp、mvvm使用关系总结 原创 2016年03月27日 10:04:21 标签:android /架构 9837 MVC MVC全名是Model View Controller,
- 电商系统Broadleaf文档翻译(四) - 应用架构组件视图application architecture component view