本文主要从源码角度介绍架构组件ViewModel的实现原理,默认读者已经熟悉MVVM设计模式。
功能与使用场景
在分析原理之前,简单的过一遍功能以及结合具体场景介绍实现原理会更容易理解。
主要功能
在MVVM的模式中,ViewModel的作用在于维护数据与发送消息。但是当前要讨论的架构组件ViewModel并没有提供这两个功能的支持,需要我们自己去继承然后实现这些功能。
架构组件ViewModel实际上提供的功能是:
- 控制ViewModel的生命周期,使之与Activity与Fragment的生命周期绑定。
- 虽然与Activity的生命周期绑定,但可以做到不随着屏幕的旋转而销毁。
- 提供便利的获取ViewModel的方法,使得ViewModel可以更便利的在不同的View类中被访问。
使用场景
一个Activity中有两个View,这两个View需要共享同一个ViewModel,此时 用这个架构组件来实现MVVM中的ViewModel就会比较容易完成模型共享的需求。
整体结构图
相关的类不多,类结构图如下:

简单介绍一下关键类的作用,ViewModelStore用于存取储ViewModel,ViewModelProvider则负责ViewModel的实例化。ViewModelProviders与ViewModelStores则是获取ViewModelProvider与ViewModelStore的便利类。
源码分析
源码分析会主要围绕几个问题展开,具体如下:
ViewModel如何实例化?
ViewModel实例化的入口在ViewModelProvider的get()方法中,裁剪后的代码如下
1 | public <T extends ViewModel> T get(String key, Class<T> modelClass) { |
注意到最终的实现应该在Factory中,这里取其中的一种实现AndroidViewModelFactory为例,其create()方法如下:
1 | public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { |
注意到本质上其实就是利用了反射根据class对象实现实例化。同时也可以发现ViewModel和Factory的类型其实是一一对应的,这样的设计可以方便使用者根据需要扩展ViewModel中的必需字段。例如AndroidViewModel就是相对ViewModel多了Application字段的一种扩展实现。
ViewModel如何存取与回收?
ViewModel的存取是通过ViewModelStore来实现的,下面是ViewModelStore的简略实现:
1 | public class ViewModelStore { |
易知ViewModelStore的存取功能本质上是通过HashMap实现的。
再看下ViewModelStore的实例化过程,前面提到过ViewModelStore与Fragment以及Activity都是一一对应的,以Fragment为例,它实现了ViewModelStoreOwner接口的getViewModelStore()方法,持有ViewModelStore实例,并负责向外提供,代码如下:
1 | public ViewModelStore getViewModelStore() { |
因为ViewModelStore与Fragment以及Activity是一一对应的,因此在销毁的时候,我们需要一并回收相关资源,具体的实现也比较简单,就是在onDestory()方法中调用ViewModelStore的clear()方法,不过这里有另外一些细节,下一节再讨论。
总结一下,ViewModel的存取是以ViewModelProvider的接口为入口,最终调用到ViewModelStore完成存取,期间用到的实例均是懒加载实现的单例。
ViewModel为什么可以实现旋转屏幕不销毁?
众所周知,在屏幕旋转或者其他一些系统级设置修改的时候(如系统语言),Activity会被销毁重建。如果这时候ViewModel也被回收,则意味着我们需要重新去获取一遍数据,浪费了资源。Google团队考虑到了这一点,因此ViewModel在这种场景下是不会被回收的,下面来看下具体的实现原理。
ViewModel相关的代码在两个不同的包里,ViewModelProvider等基础类在viewmodel的基础包中,而ViewModelProvider等扩展类则在扩展包中。因此旋转屏幕不销毁也有两套不同的实现。
1、基础包的实现
在基础包的实现中ViewModelStore时存储在FragmentActivity中的,相关的关键代码如下:
1 | public class FragmentActivity implements ViewModelStoreOwner { |
总结一下就是利用onRetainNonConfigurationInstance()方法在旋转屏幕时保存ViewModelStore实例,并在重新创建时重新赋值。
2、扩展包的实现
扩展包的实现重点在于HolderFragment的使用,当我们使用ViewModelProviders去获取ViewModel实例时,会导致ViewModeStore存储在HolderFragment中,而不是我们输入的Activity或Fragment中,这个无视图的HolderFragment用于存储ViewModelStore实例,并且可以做到在旋转屏幕时不被回收,最终实现ViewModel的实例存储。
看下它的关键代码:
1 | public class HolderFragment extends Fragment implements ViewModelStoreOwner { |
关键点在于setRetainInstance(true)方法的调用,根据文档的提示,该方法可以保证Fragment在旋转屏幕的时候不被销毁。
void setRetainInstance)(boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change).
剩下的逻辑无非是Framgment的add与复用,这里不再赘述。同时上面的代码是以Activity为例介绍的,与Fragment相关的实现也是大同小异,这里不再赘述。