Google发布Jetpack已经好几年了,你一直在使用里面的控件。你清楚它的工作原理么,为什么我们可以通过ViewModelProvider获取对应的ViewModel对象,还有为什么不能new一个。同时为什么ViewModel不会因为Activity和Fragment的重建,配置改变(如屏幕旋转)等,依然能够维持内部的数据。按照官方的说法就是,ViewModel是一个类,负责为Activity和Fragment准备和管理数据的。它还可以处理Activity和Fragment与应用程序其余部分的通信。下面我们就通过这篇文章学习一下其原理。
我们先从ViewModel说起,我们从源码处得知。ViewModel是一个抽象类,其中一个重要方法就是clear方法。我们看下源码:
位置1处,该标量用于标识该ViewModel对象是否回收掉。位置2处,是暴露的方法,可以在页面的对应生命周期方法里进行调用来释放掉资源。
对于Android开发来说,Google又提供了其实现类AndroidViewModel,其实实现类比较简单,就是子类需要提供Application作为其构造器参数。
说到这里,Google也是费尽心思了,为了推广Kotlin,其又提供了简单初始化ViewModel的方法。
Private val mHomeViewModel:HomeViewModel by viewModels()
想要在Activity或者Fragment中使用的话,需要引入相应的程序包:
或者
下面我们以Activity为例分析一下其原理:
首先看一下ActivityViewModelLazy类,我们看到其代码极其简单。
该类位于activity-ktx包里,主要是为了方便我们在Activity中使用ViewModel做的封装,方便初始化其对象。位置1处,我们不难猜测对于Activity而言,主要是在ComponentActivity中做的封装;位置2处,为如果参数传递进来的factory为空的话就使用默认的defaultViewModelProviderFactory对象,这里的逻辑我们一会再说。位置3处是通过ViewModelLazy类初始化一个ViewModel对象,其位于
很明显也是为了方便我们使用Kotlin编程。我们看一下源码吧。
位置1处,说明了该类主要用于在Activity和Fragment中通过辅助包来获取ViewModel对象;位置2处,是我们需要获取的ViewModel的class对象;位置3处,用来获取ViewModelStore的方法;位置4处是用来获取ViewModelProvider.Factory的方法。关于上面提到的几个类我们暂时不在这里讨论,接下来将会进一步分析。来看一下位置5处,就是需要获取的ViewModel对象。
下面我们就逐个分析下ViewModelStore,ViewModelProvider两个类。我们先来看一下ViewModelStore类的源码:
综合的说一下就是,ViewModelStore就是用键值对来存储ViewModel对象的。我们来看一下具体方法。位置1处,就是用来存储ViewModel对象的集合。位置2处就是通过键值对将对象进行存储,同时将旧的ViewModel进行更新。同时旧的对象调用onCleared方法就行资源的释放。位置3处,就是对不再使用的ViewModel进行销毁等。
接下来我们看一下ViewModelProvider类的源码。该类有三个构造器,
构造器1,只需要传递ViewModelStoreOwner对象,对于Activity和Fragment也就是对象本身就是ViewModelStoreOwner,当然不引用activity-ktx或者fragment-ktx,也可以通过ViewModelProvider(owner).get(VM.class)获取对应的ViewModel对象实例。我们看一下ViewModelStoreOwner源码,其是一个接口,官方的解释是其定义了一个范围用来获取对应的ViewModelStore.那么一切就明了了,也就是说需要在对应的Activity和Fragment实现该接口,就可以获取ViewModel对象了。关于Activity和Fragment中源码逻辑的分析会在接下来的文章中进行。我们看一下构造器中的owner参数,HasDefaultViewModelProviderFactory是一个接口用来获取默认的的ViewModelProvider.Factory。NewInstance.getInstance也是一个默认的ViewModelProvider.Factory的单例对象。
下面我们来看一下构造器2和构造器3没有太多可以说明的点,也就是说ViewModelProvider需要两个关键参数,一是factory,二是store。
下面我们看一下获取ViewModel的关键方法也就是get方法,通过key和对应的Class对象来获取对象实例。
此方法是ViewModel中最核心的方法。在标签1之前首先通过其容器mViewModelStore通过键值来获取了一个ViewModel对象,那么mViewModelStore是怎么来的,也就是实现了ViewModelStoreOwner接口的Activity和Fragment提供的。标签1处是检测获取的ViewModel对象是否和modelClass是同一类型。如果true的话,然后再判断mFactory等逻辑。标签2处是判断mFactory是否为KeyFactory的子类,来获取ViewModel对象。同时我们关注到KeyFactory是一个静态抽象类。最后通过mViewModelStore的put方法存储该对象。
下面我们回到ActivityViewModelLazy类中代码,这里的defaultViewModelProviderFactory来自于ComponentActivity,我们看一下关键源码:
这里也验证了我们的想法,对于Activity而言,其实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory两个接口。
我们先看一下ViewModelStoreOwner的具体实现:
该方法是实现接口的方法,关键是后续执行了ensureViewModelStore方法;标签2处,NonConfigurationInstances是页面配置的类,可以用来获取上一次保存的ViewModel对象,从而实现数据管理的功能。关于数据管理相关的逻辑,后续会继续介绍。我们看一下标签3处,就是用来初始化mViewModelStore对象,来存储ViewModel实例。
下面我们来看一下HasDefaultViewModelProviderFactory的接口实现,
此处方法实现就是前面提到的activity-ktx中提到的ActivityVIewModelLazy.kt中的defaultViewModelProviderFactory。下面看一下方法内容,主要是通过创建SavedStateViewModelFactory来获取Factory对象。进入SavedStateViewModelFactory构造器,我们发现其mFactory取决于application是否为null,若不为空,则为ViewModelProvider.AndroidViewModelFactory.getInstance(application),否则为ViewModelProvider.NewInstanceFactory.getInstance()。现在回过去看从ActivityViewModelLazy.kt到ViewModelProvider的ViewModel的逻辑,就十分的清楚了。
最后我们看一下ViewModel是怎么做到数据管理的。上面的方法就是答案。首先说一下方法里的onRetainCustomNonConfigurationInstance方法是为了保证旧版本的兼容性,目前已经废弃。标签1处,mViewModelStore就是通过getViewModel获取,也就是通过方法里的getLastNonConfigurationInstance方法获取的.同时通过后续方法中的代码把本次获取的ViewModelStore进行缓存,从而实现数据管理的功能。我们通过查看Activity的源码知悉,onRetainNonConfigurationInstance该方法是在Activity生命周期的onStop方法和onDestory方法之间由系统调用,同时在创建新的Activity时使用getLastNonConfigurationInstance方法获取需要的状态信息。
至此,关于Activity中ViewModel管理数据状态的原理已经讲完了,关于Fragment的大家可以自行阅读源码,道理是一致的,就不在过多的描述。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)