本文已参与「新人创作礼」活动,一起开启掘金创作之路
本文为源码学习文章之一,记录源码的阅读思考
从界面控制器逻辑中分离出视图数据所有权的做法更易行且更高效
缘分渊源 - Intro
在源码学习 - LiveData一文中提到界面层的界面控制器可以通过订阅ViewModel中的LiveData数据变更来更新View,为什么MVI框架中会推荐使用ViewModel呢?考虑到Android框架下官方提到的下面的问题
Android 框架可以管理界面控制器(如 activity 和 fragment)的生命周期。Android 框架可能会决定销毁或重新创建界面控制器,以响应完全不受您控制的某些用户操作或设备事件
- 如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失
- 界面控制器经常需要进行可能需要一些时间才能返回的异步调用
如果不能对数据管理进行拆分,界面控制器中的代码将越来越膨胀;同时增加的业务复杂度也会大大增加测试的难度。因此官方在架构组件中为界面控制器提供了ViewModel这一辅助程序类,该类旨在以注重生命周期的方式存储和管理界面相关数据,对上述问题进行了解决处理。那么本文就来看看ViewModel是如何运作的。
准备工作 - Depends
- 依赖版本
dependencies {
def lifecycle_version = "2.5.1"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
}
源码源码源码 - Code
ViewModel
ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).
跟LiveData一样,ViewModel同样属于架构组件/界面层库/生命周期感知型组件,是Android Jetpack的一部分。其职责主要为两方面
- 准备和管理提供给到界面控制器的数据 - preparing and managing the data for
- 处理控制管理器与应用其余部分的通讯交流 - handles the communication
可以看到,这里提到的是职责而非能力,是因为ViewModel是abstract class,定位上是一个框架的入口点容器。其能力提供是由业务方继承该类的实现类来实现提供。ViewModel类主要提供了onCleared函数回调来提供一个时机来进行销毁清理,以避免内存泄漏的风险,这里是手动添加销毁清理;除此之外,还提供了addCloseable接口,添加实现了Closeable接口的实现对象,可以在onClear执行前调用执行清理。以上就是ViewModel的代码实现了,东西不多,但是注意在官方介绍中还提到ViewModel是有生命周期的,如下图
这是如何实现的呢,在ViewModel的构建函数上有这么一句注释
You should never manually construct a ViewModel outside of a androidx.lifecycle.ViewModelProvider.Factory.
看起来原因在于ViewModel的创建上,看看参考例子的创建代码
final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
通过ViewModelProvider的创建顺藤摸瓜,可以得到如下一个概览框图。
界面控制器实现了ViewModelStoreOnwer接口,将自己作为ViewModelProvider的ViewModelStoreOnwer参数创建出了ViewModelProvider的实例,然后界面控制器通过调用ViewModelProvider实例,以ViewModel的实现类作为参数传入获取了需要使用的ViewModel实例。
ViewModelProvider
注意获取ViewModelProvider使用的是get而非create,是因为ViewModelProvider通过ViewModelStore缓存了ViewModel实例,而不仅仅是创建出一个新的实例。有缓存的好处在于,复杂页面的Fragment界面控制器可以共享同一个ViewModel,这个对于复杂页面的开发实现是有极大的便利的。
ViewModelStore
ViewModelStore提供了缓存ViewModel实例的能力,同时提供了clear函数用于清理存储的缓存数据,同时通知缓存的ViewModel实例执行自我清理,原来ViewModel的清理是在这里调用的,那又是谁在调用ViewModelStore的清理呢?
ViewModelStoreOwner 可以看到是ViewModelStoreOwner持有了ViewModelStore,但是ViewModelStoreOwner是一个接口,仅仅提供了获取ViewModelstore实例的能力。
ViewModelStore getViewModelStore()
那我们只能从ViewModelStoreOwner的实现方,界面控制器下手了,通过Activity的实现代码来看(Fragment的实现情况类似)。作为AppCompatActivity和FragmentActivity这两大基础Activity类的父类的ComponetActivity,实现了ViewModelStoreOwner接口(红框部分)提供了获取ViewModelStore的能力,同时也实现了LifecycleOwner接口,一下子跟生命周期感知关联了起来
因此继而可以看到下图251行代码,在生命周期状态到ON_DESTROY的时候调用了ViewModelStore的Clear清理。那么ViewModel的自动清理流程就显而易见了。
注意当只是configuration当变化导致Activity重建的时候,ViewModel是不会被清理掉的,这就有效的避免了数据的保存恢复操作,尤其是存在复杂的页面数据的时候,不用在onSaveInstance做保存的成本,实在是很吸引人的使用点。
综上,虽然ViewModel本身没有进行生命感知,但是实际上ViewModel在界面控制器中创建的时候就已经决定了其生命周期的起点,而创建ViewModel实例所需要的ViewModelStoreOwner实现方同时也实现了LifecycleOwner接口,在其销毁阶段会主动清理销毁所持有的ViewModelStore,而ViewModelStore的清理销毁会触发ViewModel的销毁清理,这也就决定了ViewModel的存在的最大时长。于是ViewModel也就具有了生命感知能力。
同时ViewModelStore是由ViewModelStoreOwner提供的,而Fragment是可以获取其依托的Activity的,那么使得同一Activity上fragment间共享数据状态的操作变得简便起来。
小白成长 - Sum
ViewModel可以说是一个很棒的框架实现,非常有效的解决了MVP模型中P的实现由于对生命周期的无感知导致的销毁,重建恢复等操作带来的复杂度;而且代码结构简单,开发者可以无需理解其框架实现逻辑就能快速上手使用,这些使得应用开发的开发测试成本大大降低。最后,整个viewModel框架都是通过简单的接口定义实现,没有特别复杂的代码实现逻辑,最后组合起来,实现了一个强大框架能力,是一个很棒的学习样例。