Android Jetpack组件架构:ViewModel的原理
导言
本篇文章是关于介绍ViewModel的,由于ViewModel的使用还是挺简单的,这里就不再介绍其的基本应用,我们主要来分析ViewModel的原理。
ViewModel的生命周期
众所周知,一般使用ViewModel是用来解决两个问题的,第一个就是关于设备配置发生改变时Activity先前状态的保存,在ViewModel出来之前我们一般会使用saveInstanceState
这个Bundle来进行状态的保存,但是这样做能存储的数据是有限的,结构也不够明确,ViewModel作为一个生命周期大于Activity的组件就可以帮我们实现状态的存储,下面是ViewModel生命周期与Activity对比的图:
可以看到直到Activity被完全Destory时ViewModel中的数据才会被清除。
我们使用ViewModel的第二个原因就是用来实现MVVM架构,可以通过DataBinding组件和ViewModel组件以及LiveData组件一起实现MVVM架构,这样可以减轻Activity的职责,避免Activity过于臃肿。
获得ViewModel的提供者
我们从ViewModel的创建开始分析其原理,这里用我们上一篇文章的例子:
mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
这里首先会通过ViewModelProvider
的构造方法获得一个ViewModelProvider的实例,其构造方法如下所示:
public constructor(owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))constructor(private val store: ViewModelStore,private val factory: Factory,private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
)
可以看到我们调用的是第一个构造方法,最终会调用到第二个构造方法中,也就是主构造方法中,这个构造来说就是指定了三个成员变量,分别是ViewModelStore
,Factory
和CreationExtras
三个类型的参数,我们先来分别介绍一下这三个类型。
ViewModelStore – ViewModel的拥有者
着整个类比较小,但是也是有注释的,我们先来看看注释:
这段注释中比较重要的信息就是ViewModel是真正用来存储ViewModel的类并且在configuration changes
就是配置发生改变时新的实例和旧的实例中的信息是一致的。只有当持有者不再会被recreated时里面的数据才会通过clear
清除。
接下来我们来看该类的源码:
public class ViewModelStore {private final HashMap<String, ViewModel> mMap = new HashMap<>();final void put(String key, ViewModel viewModel) {ViewModel oldViewModel = mMap.put(key, viewModel);if (oldViewModel != null) {oldViewModel.onCleared();}}final ViewModel get(String key) {return mMap.get(key);}Set<String> keys() {return new HashSet<>(mMap.keySet());}public final void clear() {for (ViewModel vm : mMap.values()) {vm.clear();}mMap.clear();}
}
可以看到具体是用一个哈希表来存储viewModel
的实例的,里面唯一比较大的方法就是put
方法,里面做的处理就是将替换出来的旧的viewModel实例给清理掉。
Factory – ViewModel的创建工厂
这个Factory是一个定义在ViewModelProvider的内部接口,它的主要职责是用来初始化ViewModel,说实话就是一个工厂。我们来看其定义:
public interface Factory {public fun <T : ViewModel> create(modelClass: Class<T>): T {throw UnsupportedOperationException("Factory.create(String) is unsupported. This Factory requires " +"`CreationExtras` to be passed into `create` method.")}public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =create(modelClass)companion object {@JvmStaticfun from(vararg initializers: ViewModelInitializer<*>): Factory =InitializerViewModelFactory(*initializers)}}
不过这里是一个抽象的,我们来找一个具体的,也就是defaultFactory
方法获取的工厂:
public companion object {internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =if (owner is HasDefaultViewModelProviderFactory)owner.defaultViewModelProviderFactory else instance......
}
这个方法的逻辑就是判断ViewModel的持有者是不是有默认的工厂方法,如果有的话就获取持有者的默认工厂,否则返回的是自身的instance
实例,至于这个instance
实例是在NewInstanceFactory
这个实现了Factory接口的工厂类中定义的伴生变量,具体逻辑是:
@JvmStatic
public val instance: NewInstanceFactory@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)get() {if (sInstance == null) {sInstance = NewInstanceFactory()}return sInstance!!}
可以看到instance
是一个静态的单例,他具体指向的还是这个NewInstanceFactory
类,至于它是如何初始化/创建ViewModel的实例的我们可以看一眼它的create
方法:
override fun <T : ViewModel> create(modelClass: Class<T>): T {return try {modelClass.newInstance()} catch (e: InstantiationException) {throw RuntimeException("Cannot create an instance of $modelClass", e)} catch (e: IllegalAccessException) {throw RuntimeException("Cannot create an instance of $modelClass", e)}
}
这个传入的modelClass
就是我们传入的.class文件:
ViewModelProvider(this).get(SimpViewModel::class.java)
所以可以看到,这个默认情况下的工厂就是直接用反射生成了对应ViewModel的实例。
CreationExtras – 构建ViewModel时的额外参数
首先我们来看一看注释的内容:
简单来说它就是为工厂生成实例的时候提供额外参数的,这些参数使用一个Map来存储的了,不过默认情况下我们并不需要实现额外的工厂,所以这个类型我们先略过。
获得ViewModel的实例
前面我们已经知道了通过构造ViewModelProvider我们可以获得其Factory了,接下来继续向下看:
ViewModelProvider(this).get(SimpViewModel::class.java)
我们看一看get
方法做了什么:
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {val canonicalName = modelClass.canonicalName?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")return get("$DEFAULT_KEY:$canonicalName", modelClass)}public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {val viewModel = store[key]if (modelClass.isInstance(viewModel)) {(factory as? OnRequeryFactory)?.onRequery(viewModel)return viewModel as T} else {@Suppress("ControlFlowWithEmptyBody")if (viewModel != null) {// TODO: log a warning.}}val extras = MutableCreationExtras(defaultCreationExtras)extras[VIEW_MODEL_KEY] = keyreturn try {factory.create(modelClass, extras)} catch (e: AbstractMethodError) {factory.create(modelClass)}.also { store.put(key, it) }}
可以看到第一个方法会调用第二个方法,其中第一个方法向第二个方法传入的的第一个String类型的参数是通过DEFAULT_KEY
和我们传入的类的类名拼接而成的。然后跳转到第二个方法之中去,首先会尝试从ViewModleStroe中获取对应Key对应的ViewModel,但是一般第一次创建时应该会为null
,所以之后跳转的应该是最后return块
中的factory.create
方法之中,这个方法我们在之前Factory的介绍中提到过了,具体就是通过反射实例化ViewModel的,并且最后将其放入到ViewModelStore对象之中去。
至于多次获取同一个ViewModel实例是会跳转到:
if (modelClass.isInstance(viewModel)) {(factory as? OnRequeryFactory)?.onRequery(viewModel)return viewModel as T}
这一段中去,这里onRequery
默认是无实现的,也就是说并不会对viewModel做任何的处理。
ViewModelStore在哪里被创建
既然ViewModel是被存储在ViewModelStore之中的,那ViewModelStore究竟是在哪里被创建出来的呢?我们可以在ComponentActivity
之中找到答案:
public ViewModelStore getViewModelStore() {if (getApplication() == null) {throw new IllegalStateException("Your activity is not yet attached to the "+ "Application instance. You can't request ViewModel before onCreate call.");}ensureViewModelStore();return mViewModelStore;
}void ensureViewModelStore() {if (mViewModelStore == null) {NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance(); //获得上次配置更改的相关参数if (nc != null) { //当存在上次的参数时// Restore the ViewModelStore from NonConfigurationInstancesmViewModelStore = nc.viewModelStore; //恢复上次的参数}if (mViewModelStore == null) {mViewModelStore = new ViewModelStore(); //创建一个新的ViewModelStore}}
}
可以看到这里在获得ViewModelStore时主要是通过一个NonConfigurationInstances
,而该参数是一个静态的对象,也就是说,它是一个单例,这样就保证了Activity在整个生命周期之中只有一个ViewModelStore实例,从而实现配置改变时也可以恢复数据的作用。
Activity的默认工厂
在看ComponentActivity的源码时,发现了原来Activity也是有默认工厂的,它的具体实现如下:
constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {savedStateRegistry = owner.savedStateRegistrylifecycle = owner.lifecyclethis.defaultArgs = defaultArgsthis.application = applicationfactory = if (application != null) getInstance(application)else ViewModelProvider.AndroidViewModelFactory()}
这个方法最终设置到的工厂类都是一个名为AndroidViewModelFactory
的工厂类:
public open class AndroidViewModelFactoryprivate constructor(private val application: Application?,// parameter to avoid clash between constructors with nullable and non-nullable// Application@Suppress("UNUSED_PARAMETER") unused: Int,) : NewInstanceFactory() {@Suppress("SingletonConstructor")public constructor() : this(null, 0)@Suppress("SingletonConstructor")public constructor(application: Application) : this(application, 0)@Suppress("DocumentExceptions")override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {return if (application != null) {create(modelClass)} else {val application = extras[APPLICATION_KEY]if (application != null) {create(modelClass, application)} else {// For AndroidViewModels, CreationExtras must have an application setif (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {throw IllegalArgumentException("CreationExtras must have an application by `APPLICATION_KEY`")}super.create(modelClass)}}}@Suppress("DocumentExceptions")override fun <T : ViewModel> create(modelClass: Class<T>): T {return if (application == null) {throw UnsupportedOperationException("AndroidViewModelFactory constructed " +"with empty constructor works only with " +"create(modelClass: Class<T>, extras: CreationExtras).")} else {create(modelClass, application)}}@Suppress("DocumentExceptions")private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {//如果传入的Class类型实现的接口和AndroidViewModel::class一致,及说明也有生命周期,调用构造犯法并且传入application对象try {modelClass.getConstructor(Application::class.java).newInstance(app)} catch (e: NoSuchMethodException) {throw RuntimeException("Cannot create an instance of $modelClass", e)} catch (e: IllegalAccessException) {throw RuntimeException("Cannot create an instance of $modelClass", e)} catch (e: InstantiationException) {throw RuntimeException("Cannot create an instance of $modelClass", e)} catch (e: InvocationTargetException) {throw RuntimeException("Cannot create an instance of $modelClass", e)}} else super.create(modelClass)}public companion object {internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =if (owner is HasDefaultViewModelProviderFactory)owner.defaultViewModelProviderFactory else instanceinternal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"private var sInstance: AndroidViewModelFactory? = null@JvmStaticpublic fun getInstance(application: Application): AndroidViewModelFactory {if (sInstance == null) {sInstance = AndroidViewModelFactory(application)}return sInstance!!}private object ApplicationKeyImpl : Key<Application>@JvmFieldval APPLICATION_KEY: Key<Application> = ApplicationKeyImpl}}
具体通过getInstance方法
就跳转到了这里,可以发现似乎工厂类都是一个单例的模式,这个工厂的特殊之处就是他的create
方法涉及到了Application对象的传入,比如说这里的newInstance
方法:
modelClass.getConstructor(Application::class.java).newInstance(app)public T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException
{if (serializationClass == null) {return newInstance0(initargs);} else {return (T) newInstanceFromSerialization(serializationCtor, serializationClass);}
}
也就是说整个被传入Application的生命周期内都只有一个实例,这样由于创建的实例在生命周期范围内的单例性和ViewModelStore的单例性,整个ViewModel就可以实现在整个Activity的生命周期内(发生意外,比如说配置改变时)数据不变更的作用。