源码解析Android Jetpack组件之ViewModel的使用
作者:孙先森Blog
前言
在之前 LiveData 源码浅析的博客中提到了 ViewModel 组件,当时对 ViewModel 的解释是 “生命周期比Activity” 更长的对象。本文就来了解下其实现原理。
依赖版本
// 注意这里的 appcompat、activity-ktx、fragment-ktx // 高版本的自动引入了 viewmodel-savedstate 实战中很少用到的功能 // 篇幅原因 就不再本文中分析 viewmodel-savedstate 扩展组件了 implementation 'androidx.appcompat:appcompat:1.0.0' def fragment_version = "1.1.0" def activity_version = "1.0.0" implementation "androidx.activity:activity-ktx:$activity_version" implementation "androidx.fragment:fragment-ktx:$fragment_version" def lifecycle_version = "2.5.1" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
基础使用
定义
class MainViewModel: ViewModel(){ ... } // or class MainViewModel(application: Application): AndroidViewModel(application){ val data: String = "" fun requestData(){ data = "xxx" } }
在 MainVieModel 中可以定义 UI 界面中需要的数据(对象、LiveData、Flow 等等)和方法,在 Activity 真正销毁前 ViewModel 中的数据不会丢失。
Activity 中获取
val vm = ViewModelProvider(this).get(MainViewModel::class.java) // or // 引入 activity-ktx 库可以这样初始化 ViewModel val vm by viewModels<MainViewModel>() // 通过 vm 可以调用其中的方法、获取其中的数据 vm.requestData() Log.e(TAG, vm.data)
Fragment 中获取
val vm = ViewModelProvider(this).get(MainViewModel::class.java) // or // 获取和 Activity 共享的 ViewModel 也就是同一个 ViewModel 对象 val vm = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
引入 fragment-ktx 可以这样初始化
val vm = viewModels<MainViewModel>() // or 效果同上 val vm = activityViewModels<MainViewModel>()
前置知识
ViewModel 的使用非常简单,也很容易理解,就是一个生命周期长于 Activity 的对象,区别在于不会造成内存泄漏。ViewModel 不是魔法,站在开发者的角度在 ViewModel 没有问世之前横竖屏切换需要保存状态数据的需求通常都是通过 onSaveInstanceState、onRestoreInstanceState 来实现。
onSaveInstanceState、onRestoreInstanceState
关于这两个方法这里就简单概述一下:onSaveInstanceState 用于在 Activity 横竖屏切换(意外销毁)前保存数据,而 onRestoreInstanceState 是用于 Activity 横竖屏切换(重建)后获取保存的数据;
onSaveInstanceState 调用流程
由于是在 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 调用流程
看完了 onSaveInstanceState 的调用流程,那么 onRestoreInstanceState 的流程就来简单说说,由于在 onStart 后发生回调,所以直接去看 ActivityThread 中的源码:
ActivityThread.java
public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, ActivityOptions activityOptions) { // ... mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); // ... }
可以看出这里从 ActivityClientRecord 中取出了 activity 和 state 进行传毒,后面就和 onSaveInstanceState 调用流程一样了,源码比较简单就不贴了。
onRetainCustomNonConfigurationInstance、getLastCustomNonConfigurationInstance
除了 onSaveInstanceState 和 onRestoreInstanceState,在 Activity 中还有一组方法可以实现类似的功能,就是 onRetainCustomNonConfigurationInstance 和 getLastCustomNonConfigurationInstance,前者即保存数据,后者即获取保存的数据;
简单使用
override fun onRetainCustomNonConfigurationInstance(): Any? { val data = SaveStateData() return data } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 获取保存的数据 val data = getLastCustomNonConfigurationInstance() as SaveStateData }
和 onSaveInstanceState 使用的区别在于 onSaveInstanceState 只能在其参数中的 Bundle 对象中写入数据,而 onRetainCustomNonConfigurationInstance 返回的类型是 Any(Java Object)不限制数据类型。老样子看一下这组方法的源码调用流程。
onRetainCustomNonConfigurationInstance
onRetainCustomNonConfigurationInstance 是在 ComponentActivity 中定义的,默认实现返回 null,其在 onRetainNonConfigurationInstance 方法中被调用:
ComponentActivity.java
public Object onRetainCustomNonConfigurationInstance() { // ComponentActivity 中默认返回 null return 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; } // 新建 NonConfigurationInstances 对象 NonConfigurationInstances nci = new NonConfigurationInstances(); // custom 赋值给了 NonConfigurationInstances 对象 nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
从 ComponentActivity 的这部分源码中可以看出保存的数据最终放在了 NonConfigurationInstances 对象的 custom 属性中;接着找 onRetainNonConfigurationInstance 的定义,在 Activity 中:
Activity.java
public Object onRetainNonConfigurationInstance() { // 默认返回 null return null; } NonConfigurationInstances retainNonConfigurationInstances() { // ComponentActivity 中返回的 NonConfigurationInstances 对象 Object activity = onRetainNonConfigurationInstance(); // ... // 注意 这里有新建另一个 NonConfigurationInstances 对象 NonConfigurationInstances nci = new NonConfigurationInstances(); // ComponentActivity 中返回的 NonConfigurationInstances 对象 // 存储到了新的 NonConfigurationInstances 中的 activity 属性中 nci.activity = activity; // ... return nci; }
在 Activity 类中相当于做了一层套娃,又新建了一个 NonConfigurationInstances 对象,将 ComponentActivity 中返回的 NonConfigurationInstances 对象存了进去;
其实源码看到这里就可以了,不过本着刨根问底的原则,我们接着再看一下 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 中,只不过换了一个属性罢了。
getLastCustomNonConfigurationInstance
看完了存储的流程,简单来看看取数据的流程。既然存的时候套娃了一下 NonConfigurationInstances,那取数据的时候肯定也需要套娃:
ComponentActivity.java
public Object getLastCustomNonConfigurationInstance() { // 通过 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 方法中就已经拿到了套娃后的 NonConfigurationInstances 对象,我们都知道 Activity attach 方法是在 ActivityThread 的 performLaunchActivity 中调用,看一下源码:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // ... // 参数太多 省略了 // 可以看到是从 ActivityClientRecord 中取出传入的 activity.attach(appContext, r.lastNonConfigurationInstancesn); // ... }
小节总结
两种方式都是将数据保存到了 ActivityClientRecord 中,不同的是前者限制了 Bundle 类型,后者不限制类型(ViewModel 采用的就是后者这组方法实现),不过后者已经在源码中被标记了删除,并不影响使用,标记删除是为了让开发者们利用 ViewModel 来接管这种需求。下面我们就正式进入 ViewModel 源码。
源码分析
前置知识有点长,不过也几乎把 ViewModel 的原理说透了,ViewModel 的保存、恢复是利用了系统提供的方法,不过还有些细节还需要在源码中探索,比如:如何实现 Activity/Fragment 共享 ViewModel?接下来就来深入 ViewModel 源码。
创建
先来以 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" ) AndroidViewModelFactory.getInstance(application) } // 返回了一个 ViewModelLazy 对象,将 viewModelStore、factoryProducer 传入 return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise) }
到这里先暂停看一下 AndroidViewModelFactory 是如何初始化的,以及 viewModelStore 是什么东东:
ViewModelProvider.kt
private var sInstance: AndroidViewModelFactory? = null @JvmStatic public fun getInstance(application: Application): AndroidViewModelFactory { if (sInstance == null) { sInstance = AndroidViewModelFactory(application) } return sInstance!! }
是一个单例模式,直接对 AndroidViewModelFactory 进行实例化,再来看看 mViewModelStore
ComponentActivity.java
// 都是定义在 ComponentActivity 中的变量,默认 null 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。
回过头接着看扩展方法 viewModels 返回的 ViewModelLazy:
public class ViewModelLazy<VM : ViewModel> @JvmOverloads 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 { // 调用其 get 方法获取 ViewModel cached = it // 保存到 cached 变量 } } else { viewModel } } override fun isInitialized(): Boolean = cached != null }
这里又出现了一个陌生的对象 CreationExtras,其内部也是一个 map,可以理解为一个键值对存储对象,只不过他的 Key 是一个特殊类型。
接着查看 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 的部分了,直接去看 AndroidViewModelFactory 的 create 方法:
ViewModelProvider.kt
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 是定义在 ComponentActivity 中的,ViewModel 生命周期 “长于” Activity 的原理跟这个 ViewModelStore 脱不了干系。
恢复
前面小节提过,ViewModel 的恢复利用的是 onRetainNonConfigurationInstance 方法,ViewModelStore 又是定义在 ComponentActivity 中,那么直接去看 ComponentActivity 这部分的源码:
ComponentActivity.java
public final Object onRetainNonConfigurationInstance() { // 留给开发者使用的字段 Object custom = onRetainCustomNonConfigurationInstance(); // 获取当前 Activity 的 mViewModelStore ViewModelStore viewModelStore = mViewModelStore; if (viewModelStore == null) { // 如果为 null 则尝试获取上一次保存的数据 NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // 获取上一次存储的 viewModelStore viewModelStore = nc.viewModelStore; } } // ... NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; // 开发者用的字段 nci.viewModelStore = viewModelStore; // 保存 viewModelStore 的字段 return nci; }
再来看一看 ViewModelStore 的获取方法:
public ViewModelStore getViewModelStore() { // ... if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); // 优先从保存的数据中获取 viewModelStore if (nc != null) { mViewModelStore = nc.viewModelStore; } // 获取不到才会新建 if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
Activity 获取 mViewModelStore 时优先从 getLastNonConfigurationInstance 获取到 NonConfigurationInstances 对象,再从其中获取 viewModelStore,这样在当前 Activity 作用域中创建过的 ViewModel 都存储在 ViewModelStore 中,当需要再次使用时走 ViewModel 创建流程会直接从 ViewModelStore 中返回。
最后
再了解了 onRetainNonConfigurationInstance 这组方法之后再来探究 ViewModel 的恢复原理就很简单了,onRetainNonConfigurationInstance 也被标记为了删除,google 也希望开发者尽可能的使用 ViewModel 来保存数据(临时数据)。
onRetainNonConfigurationInstance 虽然被标记为删除,但仍然可以正常使用,相比于 onSaveInstanceState 没有了数据类型限制,但并不意味着我们可以随意存储,比较大的数据还是应该考虑持久化存储。
以上就是源码解析Android Jetpack组件之ViewModel的使用的详细内容,更多关于Android Jetpack ViewModel的资料请关注脚本之家其它相关文章!