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的创建开始分析其原理,这里用我们上一篇文章的例子:
kotlin
mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
这里首先会通过ViewModelProvider
的构造方法获得一个ViewModelProvider的实例,其构造方法如下所示:
kotlin
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
清除。
接下来我们来看该类的源码:
java
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,说实话就是一个工厂。我们来看其定义:
java
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 {
@JvmStatic
fun from(vararg initializers: ViewModelInitializer<*>): Factory =
InitializerViewModelFactory(*initializers)
}
}
不过这里是一个抽象的,我们来找一个具体的,也就是defaultFactory
方法获取的工厂:
kotlin
public companion object {
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
......
}
这个方法的逻辑就是判断ViewModel的持有者是不是有默认的工厂方法,如果有的话就获取持有者的默认工厂,否则返回的是自身的instance
实例,至于这个instance
实例是在NewInstanceFactory
这个实现了Factory接口的工厂类中定义的伴生变量,具体逻辑是:
kotlin
@JvmStatic
public val instance: NewInstanceFactory
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
get() {
if (sInstance == null) {
sInstance = NewInstanceFactory()
}
return sInstance!!
}
可以看到instance
是一个静态的单例,他具体指向的还是这个NewInstanceFactory
类,至于它是如何初始化/创建ViewModel的实例的我们可以看一眼它的create
方法:
kotlin
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文件:
kotlin
ViewModelProvider(this).get(SimpViewModel::class.java)
所以可以看到,这个默认情况下的工厂就是直接用反射生成了对应ViewModel的实例。
CreationExtras -- 构建ViewModel时的额外参数
首先我们来看一看注释的内容:
简单来说它就是为工厂生成实例的时候提供额外参数的,这些参数使用一个Map来存储的了,不过默认情况下我们并不需要实现额外的工厂,所以这个类型我们先略过。
获得ViewModel的实例
前面我们已经知道了通过构造ViewModelProvider我们可以获得其Factory了,接下来继续向下看:
kotlin
ViewModelProvider(this).get(SimpViewModel::class.java)
我们看一看get
方法做了什么:
kotlin
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] = key
return 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实例是会跳转到:
java
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
}
这一段中去,这里onRequery
默认是无实现的,也就是说并不会对viewModel做任何的处理。
ViewModelStore在哪里被创建
既然ViewModel是被存储在ViewModelStore之中的,那ViewModelStore究竟是在哪里被创建出来的呢?我们可以在ComponentActivity
之中找到答案:
kotlin
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 NonConfigurationInstances
mViewModelStore = nc.viewModelStore; //恢复上次的参数
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore(); //创建一个新的ViewModelStore
}
}
}
可以看到这里在获得ViewModelStore时主要是通过一个NonConfigurationInstances
,而该参数是一个静态的对象,也就是说,它是一个单例,这样就保证了Activity在整个生命周期之中只有一个ViewModelStore实例,从而实现配置改变时也可以恢复数据的作用。
Activity的默认工厂
在看ComponentActivity的源码时,发现了原来Activity也是有默认工厂的,它的具体实现如下:
kotlin
constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {
savedStateRegistry = owner.savedStateRegistry
lifecycle = owner.lifecycle
this.defaultArgs = defaultArgs
this.application = application
factory = if (application != null) getInstance(application)
else ViewModelProvider.AndroidViewModelFactory()
}
这个方法最终设置到的工厂类都是一个名为AndroidViewModelFactory
的工厂类:
kotlin
public open class AndroidViewModelFactory
private 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 set
if (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 instance
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
private var sInstance: AndroidViewModelFactory? = null
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
private object ApplicationKeyImpl : Key<Application>
@JvmField
val APPLICATION_KEY: Key<Application> = ApplicationKeyImpl
}
}
具体通过getInstance方法
就跳转到了这里,可以发现似乎工厂类都是一个单例的模式,这个工厂的特殊之处就是他的create
方法涉及到了Application对象的传入,比如说这里的newInstance
方法:
kotlin
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的生命周期内(发生意外,比如说配置改变时)数据不变更的作用。