前语
首先希望这篇文章假如对咱们有协助的话,可以点一个善意的赞或者收藏,这对我创造来说非常重要!先感谢咱们啦
在上一篇文章中,咱们对ViewModel的基本运用以及基本原理与因装备改变的重建进程有了一个基本了解
假如还未看过第一篇文章的盆友可以移步到:ViewModel探索(1)中观看~
上一篇只讲到 ViewModel 当页面因装备变更而重建时会进行复用,但假如是内存不足或者电量不足等体系原因导致的页面被收回时ViewModel是不会被复用的,那这个时分本篇主角SavedState组件就要出场了。
先回忆一下上篇有哪些遗留的问题:
- 为什么ViewModel可以声明带有Application与SavedStateHandle参数的结构函数?这两类的ViewModel是怎样初始化的
- 由于内存不足等原因时收回Activity后再重建,是怎样经过SavedState进行康复数据的?
1.ViewModel – SavedState组件介绍
Activity 有着一套 onSaveInstanceState 状况保存机制,旨在页面因 体系原因 被收回时可以保存状况,在页面重建后后可以康复之前的状况。
可是 ViewModel 是无法直接感知 onSaveInstanceState 被触发的机遇的。
于是乎,SavedState 这个中心组件就诞生了,它可以协助开发者在 ViewModel 中处理 Activity 和 fragment 状况保存和康复。
在页面即将被毁掉的时分,每个运用 SavedState 的 ViewModel 都会创立一个 Bundle 来存储自己的这份数据,最终这些 Bundle 会被汇总到一个 Bundle 中,然后再保存到 onSaveInstanceState(Bundle outState) 的outState 中。
当页面康复的时分,会从 onCreate(Bundle savedInstanceState) 中的 savedInstanceState 中取出本来寄存的总的那个 Bundle,然后再取出一个个的归于 ViewModel 的子 Bundle,于是咱们就能在 ViewModel 中复用之前存储的数据了 。
其实便是运用 Bundle 可以保存另一个 Bundle 这么一个特色,分层分区保存数据,让数据之间相互别离,进而便利整存整取。
2.如何声明一个带有SaveState的ViewModel
//在结构函数中声明SavedStateHandle
class MyViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
private val DATA_SAVE_KEY = "save_key";
val data = MutableLiveData<String>()
fun loadData(): MutableLiveData<String> {
if (data.value == null) {
//1.首先先从内存中测验复原数据
val memoryData = savedStateHandle.get<String>(DATA_SAVE_KEY)
if (memoryData != null) {
data.postValue(memoryData)
}
//2.再从网络中拉取最新数据
val remoteData=fetchDataFormRemote()
//3.再保存到savedState中
savedStateHandle[DATA_SAVE_KEY]=remoteData;
data.postValue(remoteData)
}
return data
}
fun fetchDataFormRemote(): String {
return "从服务器获取的数据";
}
}
咱们只需求在结构函数中增加SavedStateHandle就运用成功啦,其他什么的额定操作通通不要,可以说简略易用的捏。
咱们再看看SavedStateHandle一些核心源码
class SavedStateHandle {
//结构器,康复的数据会从该参数康复至regular中
constructor(initialState: Map<String, Any?>) {
regular.putAll(initialState)
}
//内部存储数据用的Map
private val regular = mutableMapOf<String, Any?>()
//在Activity收回时,就会触发该SAM,遍历悉数保存的数据到一个Bundle中
private val savedStateProvider =
SavedStateRegistry.SavedStateProvider {
val keySet: Set<String> = regular.keys
val keys: ArrayList<String> = ArrayList(keySet.size)
val value: ArrayList<Any?> = ArrayList(keys.size)
for (key in keySet) {
keys.add(key)
value.add(regular[key])
}
bundleOf(KEYS to keys, VALUES to value)
}
//这儿进行了操作符重载,该函数等于 savedStateHandle[key]
operator fun <T> get(key: String): T? {
return regular[key] as T?
}
//这儿进行了操作符重载,该函数等于 savedStateHandle[key] = data
operator fun <T> set(key: String, value: T?) {
regular[key] = value
}
}
可以看出来,SavedStateHandle仅仅一个数据容器,他担任了:
- 存储数据,初始化时外部会输入之前缓存的数据
- 内部完成了一个SavedStateProvider接口,担任在页面收回时保存数据
在搞清楚SavedStateHandle,接下来的大路就拉开帷幕了,咱们要搞清楚以下问题
- SavedState的组件成员以及各自的责任作用
- 当页面收回时,整个缓存的进程是怎样样的?
- 当页面康复时,是怎样把缓存康复至对应的ViewModel中?
3. SavedState的组件成员介绍
由于谷歌规划ViewModel的时分用了一堆规划模式,所以SavedState会有比较多类,假如穿插着原理中解说感觉会比较乱,还不如一开端就进行介绍比较清晰。
- SavedStateRegistryOwner:是一个接口用于获取SavedStatedRegistry目标,Activity完成了该接口
- SavedStateRegistryController: 内部维护了一个 SavedStatedRegistry,用于服务与衔接 Activity\Fragment 和SavedStatedRegistry;
- SavedStatedRegistry:数据存储、康复中心;
- SavedStateHandleController:为每个ViewModel创立出对应的SavedStateHandle,而且从数据中心提出缓存数据康复其数据;
- SavedStateHandle:单个 ViewModel 用于数据存储和康复的当地
再经过以下的UML简略看一下他们的联系,有大约一个概念
(UML图懒得自己画了,在网上找了张没水印的嘻嘻)
咱们需求重点重视的类只要两个,SavedStatedRegistry,另一个是SavedStateHandleController。
上面UML暂时有点抽象也不要紧,后边就会逐个解说,在介绍完成员之后,咱们就开端进入一个阶段,当页面收回时,ViewModel是怎样被触发数据的保存的.
4.ViewModel缓存数据的进程是怎样样的(上)?
一开端说了,SavedState其实是依靠了Activity的onSaveInstanceState-onRestoreInstanceState状况保存机制,那么,咱们就从Activity开端溯源,首先看看Activity有关SavedState的代码
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,//完成了SavedStateRegistryOwner接口
{
//初始化SavedStateRegistryController
final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
//可是实际上是从mSavedStateRegistryController中取得SavedStateRegistry
public final SavedStateRegistry getSavedStateRegistry() {
return mSavedStateRegistryController.getSavedStateRegistry();
}
//当Framework触发保存时,Activity会通知Controller预备保存
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mSavedStateRegistryController.performSave(outState);
}
}
Activity中没有太多SavedState的逻辑,也的确不应该有,否则Activity会越来越臃肿,所以委托了SavedStateRegistryController去做对应的事,而Activity沦为一个Timing侠,只担任转发.
了解到Activity所做的事之后,咱们就去看看SavedStateRegistryController做了什么?
public final class SavedStateRegistryController {
private final SavedStateRegistryOwner owner;
//Controller内部声明了SavedStateRegistry
private final SavedStateRegistry savedStateRegistry;
private SavedStateRegistryController(SavedStateRegistryOwner owner) {
this.owner = owner;
//在结构函数中初始化了SavedStateRegistry
this.savedStateRegistry = new SavedStateRegistry();
}
public final void performSave(Bundle outBundle) {
//调用savedStateRegistry的performSave函数
this.savedStateRegistry.performSave(outBundle);
}
@MainThread
public final void performRestore(Bundle savedState) {
//...
//调用savedStateRegistry的performRestore函数
this.savedStateRegistry.performRestore(savedState);
}
}
咱们可以看到Controller也仅仅一个转发层,还有其他小部分逻辑可是为了专心我就去掉了,让咱们能比较会集的看到要害当地。
那么Controller已然仅仅担任转发,那么咱们就持续往下钻,看看SavedStateRegistry做了啥,上源码
class SavedStateRegistry internal constructor() {
//...
//这儿保存了每个ViewModel的SavedStateHandle,下面会接着说ViewModel是怎样将本身的SavedStateProvider存到这儿的,现在先不打开
private val components = SafeIterableMap<String, SavedStateProvider>()
//保存数据
fun performSave(outBundle: Bundle) {
//先新建一个Bundle预备保存数据
val components = Bundle()
//...
//遍历每个ViewModel中的SavedStateHandlea,调用其saveState,将SaveStateHandle中的数据进行打包
val it: Iterator<Map.Entry<String, SavedStateProvider>> =
this.components.iteratorWithAdditions()
while (it.hasNext()) {
val (key, value) = it.next()
components.putBundle(key, value.saveState())
}
//最终将打包好的数据统一保存到outBundle中的一个固定方位,这样就保存好了
if (!components.isEmpty) {
outBundle.putBundle(SAVED_COMPONENTS_KEY, components)
}
}
}
所以SavedStateRegistry现在已知的责任便是担任将其下的ViewModel数据的保存,而且是保存到Activity供给的outBunle中
可是有一点还没说的是,components这个Map中的SavedStateProvider,是什么时分保存到SavedStateRegistry中的,这个不急 后边咱们会慢慢讲到,咱们只需求知道他保存的便是每个ViewModel中的每个SavedState中的SavedStateProvider即可~
接着咱们再回忆一下SavedStateHandle
class SavedStateHandle {
//内部存储数据用的Map
private val regular = mutableMapOf<String, Any?>()
//在Activity收回时,就会触发该SAM,遍历悉数保存的数据到一个Bundle中
private val savedStateProvider =
SavedStateRegistry.SavedStateProvider {
//SavedStateRegistry最终会触发每个SavedStateHandle中的这个代码块,担任将这个ViewModel中的savedState数据打包成Bundle传出去
val keySet: Set<String> = regular.keys
val keys: ArrayList<String> = ArrayList(keySet.size)
val value: ArrayList<Any?> = ArrayList(keys.size)
for (key in keySet) {
keys.add(key)
value.add(regular[key])
}
bundleOf(KEYS to keys, VALUES to value)
}
}
好了,保存的流程基本上讲完了,咱们知道了Activity是在什么机遇经过Controller转发至Registry,然后Registry再遍历每个SavedStateProvider进行数据保存,总结便是这样!
那咱们剩余就两个疑问了
- 数据是怎样康复的?
- Registry中的Provider是什么时分塞进去的?
让咱们接着解惑
5.数据是怎样康复的(上)?
public class ComponentActivity extends androidx.core.app.ComponentActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
//在ActivityOnCreate中将有可能前次保存的savedInstanceState回传至RegistryController中
mSavedStateRegistryController.performRestore(savedInstanceState);
//...
}
}
//...RegistryController就不再打开了,它会将savedInstanceState转发至SavedStateRegistry中
class SavedStateRegistry internal constructor() {
//...省掉其他代码
//声明一个康复数据的变量
private var restoredState: Bundle? = null
internal fun performRestore(savedState: Bundle?) {
//简略粗犷的复原!
restoredState = savedState?.getBundle(SAVED_COMPONENTS_KEY)
isRestored = true
}
}
没错!简略!粗犷!Activity将前次缓存的Bundle再塞SavedStateRegistry给正儿八经的寄存起来!然后光明磊落的结束!
What?那ViewModel中的SavedStateHandle中的数据是怎样康复的?
Good Question!咱们接下来会进行最终的解惑,我总结一下现在还没解说的疑问
- ViewModel是怎样初始化结构函数中带有SavedStateHandle参数的类的?
- SavedStateRegistry是怎样保存每个ViewModel中的Provider的?
- 每个ViewModel的SavedStateHandle数据是怎样康复的?
带着最终的疑问出发!
6.数据是怎样康复的(下)?
最终的悉数,咱们要重新从ViewModel是怎样创立的进行下手,简略回忆一下创立一个ViewModel的流程
//1.如何创立一个ViewModel
ViewModelProvider(this).get(MyViewModel::class.java)
//2.这儿不绕弯子了,Activity中是自带了ViewModelFactory,咱们直接展现
public class ComponentActivity extends androidx.core.app.ComponentActivity implements HasDefaultViewModelProviderFactory
//完成了HasDefaultViewModelProviderFactory接口而且回来了SavedStateViewModelFactory
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
}
3.
public open class ViewModelProvider {
private val factory: Factory
//当咱们调用这个结构函数的时分,其实他会调用一个defaultFactory办法
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
//喏,假如ViewModelStoreOwner相同也完成了HasDefaultViewModelProviderFactory接口,则获取该接口中回来的factory,也便是第二点中的Activity实际上运用了SavedStateViewModelFactory
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
}
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
//...省掉部分代码
//按照上面的流程,这儿的factory实际上便是SavedStateViewModelFactory
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}
上面贴了几个类的源码,可是实际上想表达的东西很简略,便是Activity初始化ViewModel是用SavedStateViewModelFactory进行初始化的。接下来悉数的疑问,只要看看SavedStateViewModelFactory的源码就通通解开了
//重点还是重视create办法
class SavedStateViewModelFactory : ViewModelProvider.OnRequeryFactory, ViewModelProvider.Factory {
//Application,在咱们在ViewModel结构函数中声明application时,便是经过这个传入的
private var application: Application? = null
//保底出产用factory,当ViewModel结构函数上没声明SavedState时则运用这个Factory进行出产
private val factory: ViewModelProvider.Factory
//持有了savedStateRegistry
private var savedStateRegistry: SavedStateRegistry? = null
constructor(application: Application?, owner: SavedStateRegistryOwner) {
savedStateRegistry = owner.savedStateRegistry
this.application = application
//保底Factory会选择默许Factory或是ApplicationFactory
factory = if (application != null) getInstance(application)
else ViewModelProvider.AndroidViewModelFactory()
}
fun <T : ViewModel> create(key: String, modelClass: Class<T>): T {
//判别是否继承了AndroidViewModel
val isAndroidViewModel = AndroidViewModel::class.java.isAssignableFrom(modelClass)
//要害逻辑,这儿解开了为什么咱们在Viewmodel结构函数中声明Application或者SavedStateHandle的时分,也是能被创立出来,是由于下面会匹配咱们的结构器,一旦符合条件则会运用结构器新建目标
val constructor: Constructor<T>? = if (isAndroidViewModel && application != null) {
//这儿不打开贴源码,经过反射判别目标Class类是否包括Application和SavedStateHandle
findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE)
} else {
//这儿判别目标Class类是否只包括了SavedStateHandle
findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE)
}
// 找不到以上的结构器,那默许是无参ViewModel,走上一篇文章中的初始化办法
if (constructor == null) {
return if (application != null) factory.create(modelClass)
else instance.create(modelClass)
}
//以下逻辑是结构函数中必定含有SavedStateHandle的
//创立出LegacySavedStateHandleController,具体等等贴源码剖析,咱们现在只需求知道该controller中包括了咱们ViewModel所需求的SavedStategHandle,
val controller = LegacySavedStateHandleController.create(
savedStateRegistry, lifecycle, key, defaultArgs
)
//无论是那种结构器,咱们都会传入controller中的handle
val viewModel: T = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application!!, controller.handle)
} else {
newInstance(modelClass, constructor, controller.handle)
}
return viewModel
}
}
经过剖析SavedStateViewModelFactory源码得知,本来咱们在ViewModel上声明结构参数,是会经过匹配结构器反射创立目标的,可是这儿又冒出了一个新的类LegacySavedStateHandleController.
只看Factory的源码咱们只知道它供给了ViewModel所需的SavedStateHandle,可是它create办法做了什么呢?它有是什么呢?现已到最终的最终了,咱们上源码
class LegacySavedStateHandleController {
private LegacySavedStateHandleController() {}
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
String key, Bundle defaultArgs) {
//...省掉其他代码
//要害逻辑:从Registry取得康复的数据!(这个key便是ViewModel的Key)
Bundle restoredState = registry.consumeRestoredStateForKey(key);
//创立ViewModel所需的SavedStateHandle目标,而且将康复的数据扔进去!
SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
//创立SavedStateHandleController,可是这个SavedStateHandleController咱们可以彻底先疏忽!咱们只需求知道他包括了咱们最想要的SavedStateHandle即可
SavedStateHandleController controller = new SavedStateHandleController(key, handle);
return controller;
}
}
//最终再回忆一下SavedStateHandle的结构函数
class SavedStateHandle {
//伴生目标,Kotlin语法,了解成静态区即可
companion object {
//创立SavedStateHandle
fun createHandle(restoredState: Bundle?, defaultState: Bundle?): SavedStateHandle {
if (restoredState == null) {
//...省掉部分逻辑
//假如缓存数据是空,则直接回来一个空的SavedStateHandleSavedStateHandle
return SavedStateHandle()
}
//开端康复数据,存储的时分是KeyList和ValueList按序保存的,所以这儿也是按Key和Value康复
val keys: ArrayList<*>? = restoredState.getParcelableArrayList<Parcelable>(KEYS)
val values: ArrayList<*>? = restoredState.getParcelableArrayList<Parcelable>(VALUES)
val state = mutableMapOf<String, Any?>()
//最终将KeyValue对应的康复到Map中
for (i in keys.indices) {
state[keys[i] as String] = values[i]
}
//将康复好的数据扔到SavedStateHandl里
return SavedStateHandle(state)
}
}
///省掉其他字段...
private val regular = mutableMapOf<String, Any?>()
//结构函数保存缓存的数据
constructor(initialState: Map<String, Any?>) {
regular.putAll(initialState)
}
}
总结一下上面代码块说了什么
- Factory经过LegacySavedStateHandleController中的Create办法做了以下事情
- 经过Registry之前保存的数据,经过当时正在创立的ViewModel的Key,取出缓存数据
- 经过SavedStateHandle的createHandle办法,复原出在Bundle中的数据,而且创立出SavedStateHandle目标
- 然后把创立好而且康复了缓存的SavedStateHandle目标塞进了一个Controller中(该Controller纯工具人)
- 然后回来该Controller至Factory中
- Factory经过结构器创立ViewModel目标,而且将上面Controller中预备好的SavedStateHandle经过结构函数塞进ViewModel中
- ViewModel Created done~
到这儿,ViewModel探索之旅算是告一段落了,本篇告知了以下问题
- 为什么ViewModel可以声明带有Application与SavedStateHandle参数的结构函数?这两类的ViewModel是怎样初始化的
- 由于内存不足等原因时收回Activity后再重建,是怎样经过SavedState进行康复数据的?
7.最终可能还有有部分疑问(最终QA环节)
Q:装备改变的重建与收回重建他们数据保存的差异是什么?
- 由于装备改变的数据,最终会被保存到ActivityRecordClient中,可是会在Activity destroy时被毁掉
- 因体系问题收回的数据,最终会被保存到ActivityRecord中,那ActivityRecord是什么?这儿留个坑,后边我会敞开一系列的Framework文章,到时分猎奇的童鞋请到我Framework专栏解惑啦哈哈哈
Q.已然用到了Bundle,那Bundle本身的限制是否有效?
没错,咱们保存的数据不能存超过 1M 的数据。