ViewModel 概览
Google官方将ViewModel定义为一种业务逻辑或屏幕级状态容器------用于封装业务逻辑,以及向UI界面提供和公开状态。它具有三个特点或者优势:
- ViewModel支持持久的保留状态:它可以缓存状态,并可在配置更改后持久保留相应状态。例如,再进行配置更改后(例如旋转屏幕时),无需重新获取数据即可直接,Activity即可从中拿出旋转之前的数据;
- ViewModel具备生命周期感知能力:它可以感知到Activity/Fragment的生命周期,通过执行操作来响应它们的生命周期状态变化,这样有助于精简代码提高程序的可维护性;
- 分离业务的UI,控制对业务的访问:ViewModel可将界面相关的业务逻辑分离出来,更好的管理和处理业务逻辑。
ViewModel的基本使用
可以直接通过继承的方式定义一个你所需要的ViewModel类:
kotlin
class MainViewModel : ViewModel() {
val name = "MainViewModel"
}
然后,您可以从 activity 访问 ViewModel,如下所示:
kotlin
class MainActivity : ComponentActivity() {
private val mainViewModel : MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
}
}
拿到ViewModel的实例之后,就可以通过对ViewModel的成员变量或者函数的调用进行数据的访问和控制。
ViewModel的创建
上文中是通过Kotlin的委托来创建ViewModel的,它本质是借助ComponentActivity
的扩展函数来实现的。其中细节和原理这里不再展开,其最终也是借助ViewModelProvider
实现创建的,对用Java下的实现如下:
scala
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainViewModel model = new ViewModelProvider(this).get(MainViewModel.class);
}
}
ViewModelProvider
的创建流程核心代码如下:
kotlin
public open class 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,
) {
// ...
}
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
}
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) }
}
ViewModel的整体创建流程是比较简洁和清晰的。 ViewModelProvider
需要三个参数:
ViewModelStore
用于存储和管理ViewModel实例Factory
创建ViewModel的工厂类CreationExtras
用于为构造函数有依赖项的ViewModel提供依赖项
可以预知到整体的创建流程:
Activity借住ViewModelStore
管理所有ViewModel,它是一个Map结构的数据,使用类名canonicalName
作为key值。创建之前首先判断ViewModelStore
是否已经包含了当前类的实例,如果有则直接返回。否则的话通过Factory
创建一个新的实例,添加到ViewModelStore
并返回给调用者。以此实现了单例模式,确保了反复调用永远之返回同一个实例。
其中,Factory
以ViewModelProvider
内置实现为例:
kotlin
public open class NewInstanceFactory : Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
modelClass.getDeclaredConstructor().newInstance()
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
}
其核心是通过反射创建对应类的实例。而CreationExtras
并不是本文的重点。这里只需要明白其核心是通过工厂类,借助反射创建实例即可。
重点关注ViewModelStore
的来源和其管理ViewModel的方式。
ViewModelStore
ViewModelStore
的代码非常简单:
kotlin
open class ViewModelStore {
private val map = mutableMapOf<String, ViewModel>()
fun put(key: String, viewModel: ViewModel) {
val oldViewModel = map.put(key, viewModel)
oldViewModel?.onCleared()
}
operator fun get(key: String): ViewModel? {
return map[key]
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun keys(): Set<String> {
return HashSet(map.keys)
}
fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
}
短短几十行代码,其核心也很明确清晰:
- 通过一个Map作为容器存储ViewModel
- 提供插入获取和清除操作
那么问题来了,ViewModelStore
是谁在持有,又是在哪里创建的呢?
回到上文的代码:
kotlin
MainViewModel model = new ViewModelProvider(this).get(MainViewModel.class);
public open class ViewModelProvider
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
ViewModelProvider
接受一个ViewModelStoreOwner
,而ViewModelStoreOwner
则是ViewModelStore
的持有者。ViewModelStoreOwner
是一个接口,其源码实现如下:
kotlin
interface ViewModelStoreOwner {
val viewModelStore: ViewModelStore
}
而它最终是在我们的Activity的父类ComponentActivity
里被实现了:
kotlin
open class ComponentActivity() : androidx.core.app.ComponentActivity(),ViewModelStoreOwner {
private var _viewModelStore: ViewModelStore? = null
override val viewModelStore: ViewModelStore
get() {
ensureViewModelStore()
return _viewModelStore!!
}
private fun ensureViewModelStore() {
if (_viewModelStore == null) {
val nc = lastNonConfigurationInstance as NonConfigurationInstances?
if (nc != null) {
_viewModelStore = nc.viewModelStore
}
if (_viewModelStore == null) {
_viewModelStore = ViewModelStore()
}
}
}
上述代码是精简之后的代码。正常流程下会走nc
为null的流程,lastNonConfigurationInstance
会在屏幕配置发生变化(如屏幕旋转)后起到数据恢复的作用。这里先关注主流程ViewModelStore
的创建。核心有两点:
- Activity是
ViewModelStore
的持有者:通过实现ViewModelStoreOwner
获得向外部提供ViewModelStore
的能力 - Activity采用了饿汉式单例创建和管理
ViewModelStore
,每当有调用者获取ViewModelStore
时,首先判断是否已经创建过。确保当前Activity内有且只有一个ViewModelStore
。
至此我们大致已经梳理清ViewModel的创建流程了,它们整体的对应关系如下:
Activity通过ViewModelStore
持有并管理ViewModel
,Activity和ViewModelStore
之间是一对一的关系。且使用了单例模式确保ViewModelStore
的唯一性。而ViewModelStore
是一个通过一个Map存储和管理所有ViewModel
,并通过ViewModelProvider
来实现单例,保证同一ViewModel
在同一Activity里只有一个。
接下来就是ViewModel的销毁流程了。
ViewModel的销毁
先贴出官方对其生命周期的结束图:
ViewModel
的生命周期与其作用域直接关联。ViewModel
会一直保留在内存中,直到其作用域 ViewModelStoreOwner
消失。它是贯穿整个Activity的生命周期的。只在Activity销毁时才会被销毁。
ViewModel
提供了onCleared
回调函数,供使用者做一些销毁工作。参考上文中ViewModelStore
的代码,销毁是在ViewModelStore
中被统一调用的:
kotlin
fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
ViewModelStore
的clear会遍历所有已经存在的ViewModel
实例,并逐个销毁。追根溯源,clear是在ComponentActivity
中被调用的:
scss
init {
lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
contextAwareHelper.clearAvailableContext()
// And clear the ViewModelStore
if (!isChangingConfigurations) {
viewModelStore.clear()
}
reportFullyDrawnExecutor.activityDestroyed()
}
})
}
Activity在初始化时,通过init函数添加Lifecycle
监听,通过Lifecycle
监听到Activity的销毁,然后调用ViewModelStore
的clear函数。Lifecycle
是通过为每个Activity添加ReportFragment
实现对Activity的生命周期监听的,这里暂不对其绑定逻辑进行分析,只展示相关声明周期回调位置:
kotlin
open class ReportFragment() : android.app.Fragment() {
override fun onStart() {
super.onStart()
dispatchStart(processListener)
dispatch(Lifecycle.Event.ON_START)
}
override fun onResume() {
super.onResume()
dispatchResume(processListener)
dispatch(Lifecycle.Event.ON_RESUME)
}
override fun onPause() {
super.onPause()
dispatch(Lifecycle.Event.ON_PAUSE)
}
override fun onStop() {
super.onStop()
dispatch(Lifecycle.Event.ON_STOP)
}
override fun onDestroy() {
super.onDestroy()
dispatch(Lifecycle.Event.ON_DESTROY)
processListener = null
}
}
Lifecycle.Event.ON_DESTROY
是在Fragment的onDestroy
中触发的,它和Activity的 onDestroy
一样最终由Activity中的performDestroy
触发。对应的可以理解为是ViewModel
在Activity的onDestroy
时被销毁。
ViewModel的恢复
到此时,还有两个疑惑点没有解释清楚:
- 上文中
viewModelStore.clear()
调用前为什么要先判断!isChangingConfigurations
- 既然ViewModel是在
performDestroy
中被销毁的,屏幕发声旋转时也会触发该函数,那ViewModel是怎么做到在屏幕旋转时不会丢失的呢?
答案就蕴藏在问题问题中,Activity正是通过isChangingConfigurations
来判断ViewModel是否需要销毁的,在我们最终所继承的Activity中有如下代码:
typescript
public class Activity extends ContextThemeWrapper {
boolean mChangingConfigurations = false;
NonConfigurationInstances mLastNonConfigurationInstances;
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
}
这里里面就涉及到了一部分Activity的生命周期流转,相关知识可以查看:Activity的生命周期流转过程。我们需要知道在屏幕发生旋转时Activity的生命周期流转都是通过handleRelaunchActivity
触发的。大致执行流程如下:
其中:mChangingConfigurations
负责标记Activity在销毁时是否调用viewModelStore.clear()
,而mLastNonConfigurationInstances
则在Activity attach
时接受来着ActivityThread
传递过来的NonConfigurationInstances
实例。
首先看mChangingConfigurations
状态标记,当屏幕旋转时,在Activity销毁之前。ActivityThread
的handleRelaunchActivity
会被触发。此时,mChangingConfigurations
会被修改为true。
ini
//ActivityThread.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
ActivityClientRecord r = mActivities.get(tmp.token);
r.activity.mChangingConfigurations = true;
}
这样在Lifecycle.Event.ON_DESTROY
被触发时,由于mChangingConfigurations
为true,viewModelStore.clear()
函数就不会执行。因此,Activity的所有ViewModel的clear
函数也不会执行。
然而,这只是ViewModelclear
函数不会执行的原因。ViewModel是通过ViewModelStore
被Activity所持有的,整个Activity都被销毁了,也意味着它所包含的所有子类都会被销毁,那么ViewModel又是怎么恢复的呢?
关键就在mLastNonConfigurationInstances
。在Activity的attach
函数被执行时,Activity都会接受一个lastNonConfigurationInstances
参数 。而这个参数有什么用呢,回顾上文中的ViewModelStore
创建流程代码:
kotlin
private fun ensureViewModelStore() {
if (_viewModelStore == null) {
val nc = lastNonConfigurationInstance as NonConfigurationInstances?
if (nc != null) {
_viewModelStore = nc.viewModelStore
}
if (_viewModelStore == null) {
_viewModelStore = ViewModelStore()
}
}
}
这段代码只有在_viewModelStore
和nc.viewModelStore
都为空时才会创建一个新的ViewModelStore
。很明显,Activity是通过借助mLastNonConfigurationInstances
保存ViewModelStore
进而实现当前Activity全部ViewModel的恢复的。接下来就是只需找到Activity是何时将ViewModelStore
存储起来的了。
它是在ComponentActivity
的onRetainNonConfigurationInstance
函数中被存储的,代码如下:
kotlin
final override fun onRetainNonConfigurationInstance(): Any? {
var viewModelStore = _viewModelStore
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
val nc = lastNonConfigurationInstance as NonConfigurationInstances?
if (nc != null) {
viewModelStore = nc.viewModelStore
}
}
if (viewModelStore == null && custom == null) {
return null
}
val nci = NonConfigurationInstances()
nci.viewModelStore = viewModelStore
return nci
}
该函数返回一个NonConfigurationInstances
实例,其持有当前Activity的ViewModelStore
实例。而onRetainNonConfigurationInstance
调用流程如下:
ini
//Activity.java
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
return nci;
}
arduino
//ActivityThread.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
ActivityClientRecord r = mActivities.get(tmp.token);
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
}
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
handleDestroyActivity(r, false, configChanges, true, reason);
}
public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
}
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
mInstrumentation.callActivityOnDestroy(r.activity);
}
ViewModelStore
在Activity销毁之前,会作为ActivityClientRecord
的实例存储到存储到 ActivityThread
的mActivities
中。
至此,ViewModelStore
的保存和恢复流程已结束,整体流程如下:
- 在屏幕旋转时,ActivityThread在销毁Activity之前通过
onRetainNonConfigurationInstance
获取当前Activity的ViewModelStore
- ActivityThread借助
ActivityClientRecord
将ViewModelStore
存储在变量mActivities
中 - 每当Activity执行
attach
函数时,ActivityThread都会从mActivities
中获取已保存的lastNonConfigurationInstances
并赋值给Activity的mLastNonConfigurationInstances
成员变量 - 最后,当需要访问ViewModel时,直接从
mLastNonConfigurationInstances
获取屏幕旋转前已经创建好的ViewModel。
总结
- Activity通过一个Map结构的
ViewModelStore
管理ViewModel,并借助ViewModelProvider
穿件ViewModel并实现单例 - Activity借助ActivityThread,通过将
ViewModelStore
实例存储在ActivityThread的成员变量mActivities
中实现ViewModelStore
的保存 - ActivityThread在调用Activity的
attach
之前,首先通过mActivities
获取到目标Activity的ActivityClientRecord
,并将其中的lastNonConfigurationInstance
传递给目标Activity。而Activity则借助其判断是否需要重新创建ViewModelStore
。并间接的实现ViewModel的恢复功能。