ViewModel类是一种业务逻辑或屏幕级状态容器。
它用于将状态公开给界面,以及封装相关的业务逻辑。
它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。
这意味着在 activity 之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。
ViewModel 的优势
ViewModel 可以替代需要手动实现 onSaveInstanceState 等方法来实现数据保存和恢复。
在 Navigation 目的地之间导航时或者 Activity 异常销毁时,需要保存数据以便后续重建时恢复。
此时,如果您不利用保存实例状态机制存储相应数据,系统便会销毁相应数据。ViewModel 提供了一个便捷的数据持久性 API,可以解决此问题。
ViewModel 主要优势实际上有两个方面:
- 它允许您持久保留界面状态。
- 它可以提供对业务逻辑的访问权限。
ViewModel 使用方法
增加相关库依赖
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.activity:activity-ktx:1.7.1'
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
ViewModel 定义
分为普通 ViewModel 和 AndroidViewModel (可以拿到 Application 实例)
class MainViewModel: ViewModel() {
//...
}
// or
class MainViewModel(application: Application): AndroidViewModel(application){
//...
}
在 MainVieModel 中可以定义 UI 界面中需要的数据(对象、LiveData、Flow 等等)和方法,在 Activity 真正销毁前 ViewModel 中的数据不会丢失。
Activity 中获取
val vm = ViewModelProvider(this).get(MainViewModel::class.java)
// 引入 activity-ktx 库可以这样初始化 ViewModel
val vm by viewModels<MainViewModel>()
Fragment 中获取
val vm = ViewModelProvider(this).get(MainViewModel::class.java)
// or
// 获取和 Activity 共享的 ViewModel
val vm = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
// 引入 fragment-ktx 可以这样初始化
val vm by viewModels<MainViewModel>()
val vm by activityViewModels<MainViewModel>()
默认数据保存恢复方法
ViewModel 的使用非常简单,也很容易理解,就是一个 生命周期长于 Activity 的对象,区别在于 不会造成内存泄漏。
ViewModel 不是魔法,站在开发者的角度在 ViewModel 没有问世之前横竖屏切换需要保存状态数据的需求通常都是通过 onSaveInstanceState onRestoreInstanceState 来实现。
onSaveInstanceState
- onSaveInstanceState 用于在 Activity 横竖屏切换(意外销毁)前保存数据;
由于是在 Activity 销毁前触发,那么直接来 ActivityThread 中找到 performPauseActivity 方法:
// ActivityThread.java
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) {
// ...
if (shouldSaveState) {
callActivityOnSaveInstanceState(r);
}
// ...
}
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
// ...
// 这里通过 ActivityClientRecord 获取到 activity
// state 是 Bundle 对象,后面要保存的数据就放在 state 中
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
// ...
}
这里有 ActivityThread 调用到了 Instrumentation 中,继续看源码:
// Instrumentation.java
public void callActivityOnSaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
activity.performSaveInstanceState(outState);
}
根据传入的 activity 调用其 performSaveInstanceState 方法:
// Activity.java
final void performSaveInstanceState(@NonNull Bundle outState) {
onSaveInstanceState(outState);
}
总结一下,onSaveInstanceState 中我们将数据存储在 Bundle 对象中,而这个 Bundle 对象是存储在 ActivityClientRecord 中。
onRestoreInstanceState
- onRestoreInstanceState 是用于 Activity 横竖屏切换(重建)后获取保存的数据;
onRestoreInstanceState 的流程就来简单说说,由于在 onStart 后发生回调,所以直接去看 ActivityThread 中的源码:
// ActivityThread.java
public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
// ...
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
// ...
}
可以看出这里从 ActivityClientRecord 中取出了 activity 和 state(bundle 数据) 进行传递,后面就和 onSaveInstanceState 调用流程类似了。
小结
Activity 在非正常销毁时,在 onStop 之前 (onPause 前后不确定) 会调用 onSaveInstanceState 来把数据保存到 bundle 中;
在 onStart 之后会调用 onRestoreInstanceState 来恢复此前保存的数据;
另外 onCreate 冲的 savedInstanceState 参数也可以获取到此前保存的数据。
override fun onCreate(savedInstanceState: Bundle?) {
//...
}
自定义数据保存恢复方法
除了 onSaveInstanceState 和 onRestoreInstanceState (Activity 中),在 ComponentActivity 中还有一组方法可以实现类似的功能,就是 onRetainCustomNonConfigurationInstance 和 getLastCustomNonConfigurationInstance,前者即保存数据,后者即获取保存的数据;
使用示例
override fun onRetainCustomNonConfigurationInstance(): Any? {
return SaveStateData()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 获取保存的数据
val data = getLastCustomNonConfigurationInstance() as SaveStateData
}
和 onSaveInstanceState 使用的区别在于 onSaveInstanceState 只能在其参数中的 Bundle 对象中写入数据,而 onRetainCustomNonConfigurationInstance 返回的类型是 Any 不限制数据类型。先来这组方法的源码调用流程。
onRetainCustomNonConfigurationInstance
onRetainCustomNonConfigurationInstance 是在 ComponentActivity 中定义的,默认实现返回 null,其在 onRetainNonConfigurationInstance 方法中被调用:
public Object onRetainCustomNonConfigurationInstance() {
return null; // 默认返回 null, 可以重写
}
public final Object onRetainNonConfigurationInstance() {
// 调用👆🏻方法,保存在 custom 变量中
Object custom = onRetainCustomNonConfigurationInstance();
// 这里可以看到 ViewModel 相关的源码了,后面再继续说
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
// 新建 ComponentActivity.NonConfigurationInstances 对象
NonConfigurationInstances nci = new NonConfigurationInstances();
// custom 和 viewModelStore 赋值给了 NonConfigurationInstances 对象
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
接着找 onRetainNonConfigurationInstance 的定义,在 Activity 中:
// Activity.java
public Object onRetainNonConfigurationInstance() {
// 默认返回 null, ComponentActivity 重写了
return null;
}
NonConfigurationInstances retainNonConfigurationInstances() {
// ComponentActivity 中返回的 NonConfigurationInstances 对象
Object activity = onRetainNonConfigurationInstance();
// ...
// 注意 这里新建的是 Activity.NonConfigurationInstances 对象
NonConfigurationInstances nci = new NonConfigurationInstances();
// ComponentActivity 中返回的 NonConfigurationInstances 对象
// 存储到了新的 NonConfigurationInstances 中的 activity 属性中
nci.activity = activity;
// ...
return nci;
}
两个 NonConfigurationInstances 数据结构对比
// ComponentActivity.java
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
// Activity.java
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
接着再看一下 NonConfigurationInstances 到底存在了哪里?在 ActivityThread.java 中找到了调用 retainNonConfigurationInstances 的地方:
// ActivityThread.java
void performDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
// ...
// 这个 r 是参数中的 ActivityClientRecord
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
}
和 onSaveInstanceState 一样存储在了 ActivityClientRecord 中,只不过是存到了另外一个属性 lastNonConfigurationInstances。
getLastCustomNonConfigurationInstance
看完了存储的流程,简单来看看取数据的流程:
// ComponentActivity.java
public Object getLastCustomNonConfigurationInstance() {
// 通过父类 Activity#getLastNonConfigurationInstance 获取 NonConfigurationInstances
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 返回 custom
return nc != null ? nc.custom : null;
}
在 Activity 中肯定还需要取一次 ActivityClientRecord 中的 NonConfigurationInstances:
// Activity.java
NonConfigurationInstances mLastNonConfigurationInstances;
public Object getLastNonConfigurationInstance() {
// 返回其 activity 字段
return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
}
// mLastNonConfigurationInstances 赋值在 attach 方法中
final void attach(Context context, /*参数太多省略了*/ NonConfigurationInstances lastNonConfigurationInstances) {
// ...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
// ...
}
可以看出在 Activity#attach 方法中就已经拿到了 Activity.NonConfigurationInstances 对象,通过源码知道 Activity#attach 方法是在 ActivityThread 的 performLaunchActivity 中调用,看一下源码:
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// ...
// 参数太多 省略了
// 可以看到是从 ActivityClientRecord 中取出传入的
activity.attach(appContext, r.lastNonConfigurationInstancesn);
// ...
}

小结
两种方式都是将数据保存到了 ActivityClientRecord 中,不同的是前者限制了 Bundle 类型,后者 不限制类型(ViewModel 采用的就是后者这组方法实现),不过后者已经在源码中被标记了废弃,并不影响使用,标记废弃是为了让开发者尽可能使用 ViewModel 来实现。
ViewModel 源码分析
从前面数据保存和恢复的分析可以看到 ViewModel 的保存、恢复是利用了系统提供的方法,不过还有些细节还需要在源码中探索,比如:如何实现 Activity/Fragment 共享 ViewModel?接下来就来深入 ViewModel 源码。
ViewModel 实例获取 by viewModels
先来以 Activity 中创建 ViewModel 的这段代码入手:
val vm by viewModels<MainViewModel>()
viewModels 扩展函数源码:
// 这是一个 ComponentActivity 的扩展方法
@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
// 从命名也可以看出是一个工厂模式,默认是 null
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
// 默认 factoryProducer 为 null, 返回的是 AndroidViewModelFactory
val factoryPromise = factoryProducer ?: {
val application = application ?: throw IllegalArgumentException(
"ViewModel can be accessed only when Activity is attached"
)
// Note: 1 🔽
AndroidViewModelFactory.getInstance(application)
}
// 返回了一个 ViewModelLazy 对象,将 viewModelStore、factoryProducer 传入
// Note: 3 🔽 // Note: 2 🔽
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
主要从上面注意点来看
- AndroidViewModelFactory 实例如何获取,
- viewModelStore 实例如何获取,
- ViewModelLazy 逻辑
1.获取 AndroidViewModelFactory
AndroidViewModelFactory 是 ViewModelProvider 内部类,实际上是一个单例模式:
// ViewModelProvider.kt -> AndroidViewModelFactory
private var sInstance: AndroidViewModelFactory? = null
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
2.获取 ViewModelStore
// ComponentActivity.java
private ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
// ...
if (mViewModelStore == null) { // 第一次启动 activity 为 null
// 获取保存的数据
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 优先从保存的数据中获取
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// 创建新的 ViewModelStore
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
ViewModelStore 内部仅仅是管理一个 Map<String, ViewModel>,用于缓存、清理创建的 ViewModel。
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);
}
//...
}
3.ViewModelLazy
再回来接着看扩展方法 viewModels 返回的 ViewModelLazy:
public class ViewModelLazy<VM : ViewModel> constructor(
private val viewModelClass: KClass<VM>, // ViewModel 的 class
private val storeProducer: () -> ViewModelStore, // 默认是 ViewModelStore
private val factoryProducer: () -> ViewModelProvider.Factory, // 这里就是 mDefaultFactory
private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty } //
) : Lazy<VM> { // 注意这里返回的 Lazy,延迟初始化
private var cached: VM? = null
override val value: VM
get() { // 由于返回的是 Lazy,也就是当使用 ViewModel 时才会调用 get
val viewModel = cached
return if (viewModel == null) { // 第一次调用是 null,进入 if
val factory = factoryProducer() // 获取 mDefaultFactory
val store = storeProducer() // 获取 ViewModelStore
ViewModelProvider( // 生成 ViewModelProvider 对象
store, factory, extrasProducer()
).get(viewModelClass.java).also {
cached = it // 保存到 cached 变量
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
接着查看 ViewModelProvider 的 get 方法是如何创建 ViewModel 的:
// 存储ViewModel的key的前缀
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
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")
// 调用重载方法,拼接 key 传入
// 当前key即为:androidx.lifecycle.ViewModelProvider.DefaultKey$com.xxx.MainViewModel
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key] // 优先从 ViewModelStroe 中获取缓存
if (modelClass.isInstance(viewModel)) { // 如果类型相同 直接返回
// 这里我们的 factory 是 AndroidViewModelFactory 所以不会走这行代码
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
}
// ...
// 这里的 defaultCreationExtras 是上一步骤中的 CreationExtras,默认值为 CreationExtras.Empty
// MutableCreationExtras 包装一层就是将 defaultCreationExtras 中所有的键值对都copy一份
val extras = MutableCreationExtras(defaultCreationExtras)
// 将当前 ViewModel 的 key 存储进去
extras[VIEW_MODEL_KEY] = key
return try {
// 优先调用双参数方法
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
// 调用双参数方法发生异常再调用单参数方法
factory.create(modelClass)
}.also {
// 获取到 ViewModel 后存储到 viewModelStore 中
// 再提一嘴 viewModelStore 是在 ComponentActivity 中定义
store.put(key, it)
}
}
终于到了创建 ViewModel 的部分了,直接去看 ViewModelProvider.AndroidViewModelFactory 的 create 方法:
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
// application 不为 null 调用单参数方法
// 在新建 AndroidViewModelFactory 已经传入了 application,一般情况不为 null
return if (application != null) {
create(modelClass)
} else {
// application 如果为 null,则会从传入的 extras 中尝试获取
val application = extras[APPLICATION_KEY]
if (application != null) {
// 这个 create 也是双参数,但不是递归,第二个参数是 application,源码贴在下面
create(modelClass, application)
} else {
// 如果 application 仍然为 null,且 ViewModel 类型为 AndroidViewModel 则抛异常
if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
throw IllegalArgumentException(...)
}
// 类型不是 AndroidViewModel 则根据 class 创建
// 注意这里调用的 super.create 是父类方法
// 父类方法直接根据 modelClass.newInstance() 创建,就一行就不贴源码了
super.create(modelClass)
}
}
}
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (application == null) { // application 为 null 直接抛异常
throw UnsupportedOperationException(...)
} else {
// 调用下面的双参数方法
create(modelClass, application)
}
}
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
// 如果是 AndroidViewModel 类型则获取带 application 的构造参数创建
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
modelClass.getConstructor(Application::class.java).newInstance(app)
} else {
// 直接调用父类 create 方法通过 modelClass.newInstance() 创建
super.create(modelClass)
}
}
总结
到这里 Activity 中的 ViewModel 实例获取就全部分析完了
- Activity 中的 ViewModel 创建都是通过单例工厂 AndroidViewModelFactory 的 create 方法中反射创建,
- 在调用 create 创建前会生成字符串 key,创建完成后会将 key 和 vm 对象存储到 ViewModelStore 中,后续获取将优先从 ViewModelStore 缓存中获取。
从前面的数据保存和恢复可以知道 ViewModelStore 实例在 Activity 意外销毁时,会保存到 ActivityClientRecord 中的 lastNonConfigurationInstances,在 Activity 重新创建时从而可以获取到原来的 ViewModelStore。
所以 ViewModel 生命周期长于 Activity 就是因为 ViewModelStore 会被保存起来。