1. 介绍
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
Android 框架可以管理界面控制器(如 Activity 和 Fragment)(记住这个术语,后面分析时会出现)的生命周期。
如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
另一个问题是,界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
简单理解:
- 页面销毁时会清除数据,重建时又会重新请求,而销毁的数据和清除的数据是一样的,这样会浪费资源
- 页面在异步请求数据过程中被销毁,而异步操作没有被取消可能会导致内存泄漏和浪费资源,例如:用户进入列表页面,在列表网络数据没有返回时退出页面,这时如果不取消网络是会造成资源浪费
- ViewModel 能感知 Activity或Fragment 的生命周期的改变,在 Activity或Fragment 销毁时执行一些数据清理工作(ViewModel 的实现类可以通过重写onCleared方法)。
2. 结论
-
Activity
持有一个ViewModelStore
,其用到的ViewModel
都存储到其中;Fragment
持有FragmentManager
,FragmentManager
持有FragmentManagerViewModel
,在FragmentManagerViewModel
中维护了Fragment
使用的ViewModelStore
;即Activity
和Fragment
都各自持有自己的ViewModelStore
,而Fragment
的ViewModelStore
间接来自Activity
-
ViewModel
能在销毁重建过程中保存数据,是因为在销毁时保存了ViewModelStore
,在重建时重新获取了旧的ViewModelStore
,再从ViewModelStore
中获取ViewModel
,进而ViewModel中持有的数据还在 - 应用处于后台时,由于内存不足导致
Activity
被销毁,这时的ViewModel
是无法恢复数据的,因为ViewModel
是暂存再应用进程,应用进程被杀掉数据也就不存在了,而onSaveInstanceState
是可以的(见文末参考)
3. 使用
ViewModel
使用非常简单
- 添加依赖,后面的源码分析也是基于此版本,不同版本源码可能不一样
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
- 业务ViewModel继承
ViewModel
或AndroidViewModel
class MineViewModel:ViewModel(){
}
-
Activity
或Fragment
中初始化ViewModel
,即可
class MainActivity : AppCompatActivity() {
private lateinit var vm: MineViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
vm = ViewModelProvider(this, defaultViewModelProviderFactory)[MineViewModel::class.java]
}
}
4. 分析
根据ViewModel
的使用和原理,分以下几个步骤:
-
ViewModel
的相关类和结构 -
ViewModel
如何实现销毁重建时保存数据的 - 同一
Activity
中的不同Fragment
是如何通过ViewModel
共享数据的
源码分析基于androidx.lifecycle:lifecycle-viewmodel:2.4.0
,ViewModelProviders
和ViewModelStores
本质上是帮助类,官方已经标记为过时类,不再分析。
4.1 ViewModel
的相关类和结构
重点关注源码包中上述图片中选中的5个类和接口。
ViewModel
ViewModel
是一个抽象类,业务类需要继承它,而它内部的变量和方法非常少,boolean mCleared
标记其是否已经销毁;void onCleared()
方法可被子类覆写,在其销毁的时候来释放资源等。
AndroidViewModel
AndroidViewModel
继承自ViewModel
,内部多了一个Application
上下文。
ViewModelStore
ViewModelStore
是用来存储ViewModel
的,界面控制器(Activity、Fragment)中持有一个ViewModelStore
。界面控制器为什么不直接持有ViewModel而是通过ViewModelStore间接持有?从ViewModelStore
的结构中就可以看出,这样设计可以允许一个界面控制器(Activity、Fragment)持有多个ViewModel
。
在界面控制器(Activity、Fragment)销毁和重建时保存和恢复的对象就是这个类型。
这里注意下 ViewModelStore 的 get 和 put 方法的声明,访问权限都是包层级的,也就表示我们使用者是无法直接通过 ViewModelStore 通过 key 拿到对应的 ViewModel 的。
内部方法实现比较简单,点进去源码看一下,都是常规的存入(put)、取出(get)、循环遍历(clear)。
ViewModelStoreOwner
ViewModelStoreOwner
是一个接口,它声明了一个 getViewModelStore
方法需要实现类实现。可以理解为ViewModelStore的持有者,一般是界面控制器(Activity、Fragment)实现此接口,职责是保留其拥有的ViewModelStore
在配置更改期间不会被销毁,也可以看作保存和恢复的关键步骤。
ViewModelProvider
ViewModelProvider
是对外提供ViewModel
的一个帮助类,内部有两个成员变量store和factory。
store由外部传入,ViewModelProvider在get方法中会通过store向其中添加ViewModel;factory是一个工厂类,ViewModelProvider中提供了默认的几个工厂实现,作用是创建ViewModel。
在使用中可以发现ViewModelProvider是直接new出来,也就是说,在销毁和重建的时候ViewModelProvider是一个新的对象,那么一个新new出来的对象是如何持有旧的数据呢?【见4.2.2 结尾】
分析一下ViewModelProvider中比较重点的方法
// 含有两个私有的final的成员变量
public open class ViewModelProvider(private val store: ViewModelStore, private val factory: Factory) {
public interface Factory {
public fun <T : ViewModel> create(modelClass: Class<T>): T
}
...
// 单参数构造方法,会调用内部默认实现的一个工厂
public constructor(owner: ViewModelStoreOwner) : this(owner.viewModelStore, defaultFactory(owner))
// 接收 ViewModelStoreOwner 和 Factory 两个参数
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(owner.viewModelStore,factory)
// 对外暴露的获取ViewModel的类,一般外部都使用该方法
@MainThread
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")
// "$DEFAULT_KEY:$canonicalName" 为构建的一个key,和目标ViewModel一一对应
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
/**
* 对外暴露的获取ViewModel的类,外部也可以调用该方法指定自定义的key
* key:用来标识ViewModel的键。
*/
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
// 从ViewModelStore中获取缓存的ViewModel
var 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.
}
}
// 若不一致或为空,使用factory创建新的viewModel
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
// * 重点:这里会将创建的viewModel加入到界面控制器(Activity、Fragment)指持有的ViewModelStore中
store.put(key, viewModel)
return viewModel
}
}
整理一下这5个类的关系
4.2 ViewModel在源码中的结构(使用、实现)
前面介绍了ViewModel相关类的源码内容和类之间的关系,那么它们在源码中是如何使用的呢?为什么业务中仅使用一句 new ViewModelProvider 就能达到如此丝滑的效果呢?接下来分析一下在源码中是如果将这几个类串联起来使用ViewModel的。
界面控制器,包含 Activity 和 Fragment,而在它们的源码中对ViewModel功能的实现又有所不同
4.2.1 分析Activity
从使用的地方作为突破口查看源码vm = ViewModelProvider(this, defaultViewModelProviderFactory)[MineViewModel::class.java]
,这段代码是初始化MineViewModel,从前面类源码中我们了解到ViewModelProvider的第一个参数类型为ViewModelStoreOwner,所以我们的Activity肯定实现了ViewModelStoreOwner接口。
经过一系列简单查找,最终找到了 androidx.activity.ComponentActivity
// androidx.activity:activity:1.2.4,源码为该版本,不同版本源码可能不同
// implements 的接口中只截取来相关的接口,实际不止一个
public class ComponentActivity extends androidx.core.app.ComponentActivity implements ViewModelStoreOwner {
// Activity 持有的 ViewModelStore,重建和恢复时就是保存的该对象
private ViewModelStore mViewModelStore;
// Activity 提供的默认工厂
private ViewModelProvider.Factory mDefaultFactory;
...
@Override
public ViewModelStore getViewModelStore() {
// 省略一个为空判断
...
// 调用该方法,内部给mViewModelStore赋值
ensureViewModelStore();
return mViewModelStore;
}
// 该方法内部给mViewModelStore赋值,也是保存和恢复ViewModelStore的关键步骤
void ensureViewModelStore() {
if (mViewModelStore == null) {
// ** 重点:从上一次的配置中读取mViewModelStore,会在[ViewModel如何实现销毁重建过程中保存数据]章节中讲解
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
// 若上一次没有数据则新建
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
...
// 提供默认的工厂
// ViewModelProvider(this, defaultViewModelProviderFactory) 中defaultViewModelProviderFactory即为该方法
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
// 内部实现省略
...
return mDefaultFactory;
}
}
至此,我们找到了Activity中持有的ViewModelStore对象及其来源,也知道销毁和重建过程中重点就在于保存这个对象。一定要记住保存的是mViewModelStore实例,mViewModelStore没有改变则数据就不会改变。为什么?重建过程中旧的Activity实例会销毁,创建新Activity实例,如果新Activity实例持有的ViewModelStore实例和旧Activity实例持有的ViewModelStore实例一致,那么ViewModelStore实例中的ViewModel中的数据就还存在,不需要再重新请求。用一张图简单说明一下:
4.2.2 分析Fragment
viewModel在Fragment中初始化,建议打开源码跟着看一遍,不然会有点晕
使用如下:
class MineFragment: Fragment() {
private lateinit var vm: MineViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vm = ViewModelProvider(this, defaultViewModelProviderFactory)[MineViewModel::class.java]
}
}
和Activity一样,Fragment肯定也实现了ViewModelStoreOwner接口,查看源码:
// androidx.fragment:fragment:1.3.6
// 省略其他接口实现
public class Fragment implements ViewModelStoreOwner {
FragmentManager mFragmentManager;
@Override
public ViewModelStore getViewModelStore() {
// 省略判断
...
return mFragmentManager.getViewModelStore(this);
}
}
这里又出现了一个mFragmentManager,(不重要)查找一下赋值的地方在哪里:
这么多地方都在赋值,怎么找,打断点吧,把断点打到成员变量上(上图中有红色眼睛的地方),这样不管是哪里赋值或者取值都会走到断点,接下来以Debug模式启动,注意启动前记得将一个Fragment添加到Activity(继承自FragmentActivity)并正确显示,否则不会走断点(没有显示的Fragment怎么会走到断点呢,你说对不对),发现是在transaction.commit()
的时候赋值的。
重新回到mFragmentManager.getViewModelStore(this)
,查看FragmentManager源码:
public abstract class FragmentManager implements FragmentResultOwner {
// * 这个很重要,是一个继承ViewModel的 final Class,目的也是要找到这个是在哪里赋值的
private FragmentManagerViewModel mNonConfig;
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
}
接下来看一下mNonConfig是在哪里赋值的,同样把断点打在成员变量上,并用Debug模式启动,查看调用栈信息:
public abstract class FragmentManager implements FragmentResultOwner {
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
// Get the FragmentManagerViewModel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
// 上图中的断点是这里,* 内部实现会把mNonConfig放到viewModelStore中,而viewModelStore来自Activity
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
...
}
}
从源码中分析attachController接收一个FragmentHostCallback<?> host
,而FragmentManagerViewModel mNonConfig
的创建和这个参数也至关重要,根据任务栈中的信息查找,可以看到FragmentHostCallback<?> host
来自onContextAvailable:143, FragmentActivity$2 (androidx.fragment.app)
:
// androidx.fragment:fragment:1.3.6
public class FragmentActivity {
// * 经过传递,传递到 FragmentManager.attachController方法中的FragmentHostCallback<?> host实际就是这里的 new HostCallbacks()
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
private void init() {
...
addOnContextAvailableListener(new OnContextAvailableListener() {
@Override
public void onContextAvailable(@NonNull Context context) {
// 任务栈断点 onContextAvailable:143, FragmentActivity$2 (androidx.fragment.app) 就是这句话
mFragments.attachHost(null /*parent*/);
...
}
});
...
}
}
还记得要找什么吗?要找FragmentManager中FragmentManagerViewModel mNonConfig
的赋值,经一系列的分析,其赋值mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
语句中的viewModelStore
来自于new HostCallbacks()
,它是FragmentActivity中的一个内部类:
public class FragmentActivity {
// 省略多余接口实现
class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements ViewModelStoreOwner{
@Override
public ViewModelStore getViewModelStore() {
// 到这里发现,Fragment中的ViewModel来自Activity
return FragmentActivity.this.getViewModelStore();
}
}
}
经过以上分析,Fragmen中的ViewModelStore实际上是来自Activity:
Fragment中的getViewModelStore()方法返回值来自其成员变量FragmentManager mFragmentManager;
FragmentManager 中的getViewModelStore(Fragment f)来自其成员变量FragmentManagerViewModel mNonConfig;
mNonConfig在初始化时FragmentManagerViewModel.getInstance(viewModelStore)又会将自身保存到host的viewModelStore中;在FragmentManagerViewModel中又维护了一个存放Fragment的ViewModelStore的Map
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
host中的getViewModelStore()来自于FragmentActivity.this.getViewModelStore();
总结一下就是,Fragment中的ViewModel来自其自身的ViewModelStore实例,ViewModelStore实例存放在FragmentManagerViewModel的成员变量中,FragmentManagerViewModel实例又被存放在Activity的ViewModelStore中。
前面有一个问题【那么一个新new出来的对象(ViewModelProvider)是如何持有旧的数据呢?】,provider虽然是新对象,但是真正取到值的是从Activity的ViewModelStore中,Activity持有的ViewModelStore实例不变,ViewMode就不变,所以provider是否为新对象不影响ViewModel及其里面的数据
4.3 ViewModel
如何实现销毁重建时保存数据的
前面的分析已经得到一个结论,界面控制器(Activity和Fragment)持有的ViewModelStore实例都来自Activity,所以在销毁过程中保存和恢复Activity的ViewModelStore实例即可达到目的。下面来分析一下如何保存和恢复。
既然是ViewModelStore实例在Activity中,那么就去分析Activity源码,看一下成员变量mViewModelStore使用的地方:
有3个方法用到mViewModelStore,其中getViewModelStore()
和ensureViewModelStore
是在初始化的时候用到,那么看一下onRetainNonConfigurationInstance()方法,该方法内部将mViewModelStore经过一系列包装做为方法的返回值:
// androidx.activity:activity:1.2.4
// 省略继承类和实现接口
public class ComponentActivity{
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
// 将mViewModelStore经过包装返回
return nci;
}
}
而在Activity的retainNonConfigurationInstances()方法中调用了上述方法:
// api 31 的源码
// 省略继承类和实现接口
public class Activity {
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
// 省略其他代码
...
}
}
而retainNonConfigurationInstances()在ActivityThread中有调用:
// api 31 的源码
public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {
// 该方法在旋转屏幕的时候会调用,可以打断点查看
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
// 省略其他代码
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) {
// 省略其他代码
// 这里面会将Activity的mViewModel放到 r.lastNonConfigurationInstances 中
handleDestroyActivity(r, false, configChanges, true, reason);
// 省略其他代码
// 该方法会从 r 恢复mViewModel
handleLaunchActivity(r, pendingActions, customIntent);
// 省略其他代码
}
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) {
// 省略其他代码
// 调用 Activity.retainNonConfigurationInstances()
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
// 省略其他代码
}
}
重点方法和调用都在上面截取,这样就找到Activity的mViewModel实例是在ActivityThread.handleRelaunchActivityInner中保存和恢复的,上面已经分析了报存的,接下来分析一下恢复,依旧是ActivityThread源码:
// api 31 的源码
public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
// 上面分析了Activity的mViewModel会放到 r.lastNonConfigurationInstances 中
handleDestroyActivity(r, false, configChanges, true, reason);
// 恢复的关键方法
handleLaunchActivity(r, pendingActions, customIntent);
}
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
final Activity a = performLaunchActivity(r, customIntent);
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 回调到这里,注意第12个参数r.lastNonConfigurationInstances,Activity的mViewModel实例在里面
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
}
}
去Activity中查看attach方法
// api 31 的源码
// 省略继承类和实现接口
public class Activity {
NonConfigurationInstances mLastNonConfigurationInstances;
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) {
// 把缓存的lastNonConfigurationInstances赋值给了成员变量mLastNonConfigurationInstances
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
}
}
在分析mViewModelStore在ComponentActivity的赋值的时候,有这么一个方法ensureViewModelStore()
([4.2.1 分析Acivity章节有源码]),其内部就调用了getLastNonConfigurationInstance()
方法取值赋值给mViewModelStore。
至此保存和恢复流程完成。重要的方法就是Activity的handleRelaunchActivityInner
方法,内部处理了销毁和重建Activity的逻辑,ViewModel处理也在其中。
4.4 Fragment
共享数据
通过分析知道,同一Activity的不同Fragment若需要共享数据,则保证ViewModel实例为同一个即可,ViewModel是放在ViewModelStore实例(通过ViewModerlStoreOwner提供)中,在初始化Fragment的ViewModel实例时,传入同一个ViewModerlStoreOwner即可,即:
vm = ViewModelProvider(this, defaultViewModelProviderFactory)[MineViewModel::class.java]
// 改为
vm = ViewModelProvider(requireActivity(), defaultViewModelProviderFactory)[MineViewModel::class.java]
// Fragment不同但requireActivity()都是其依赖的Activity同一实例,Activity内部提供的ViewModelStore也是成员变量,即可共享数据
参考:
ViewModel源码研究之聊聊onSaveInstanceState和onRetainNonConfigurationInstance的区别