Android Jetpack(3) SaveStateHandle源码阅读记录
本篇算是对接下来的ViewModel源码阅读的前置。
发现问题
由于移动端的本身的机型大小限制,注定了移动端可使用内存要比台式机少,因此移动端的系统,就必须注意内存不足这个问题,如何“带着锁拷跳舞”,让用户拥有足够良好的体验,这也成为Android系统的必修课。
如果您的应用具有缓存的进程且保留了目前不需要的资源,那么即使用户未使用您的应用,它也会影响系统的整体性能。当系统资源(如内存)不足时,它将会终止缓存中的进程。系统还会考虑终止占用最多内存的进程以释放 RAM。
https://developer.android.com/topic/performance/memory-overview?hl=zh-cn
当系统因为内存不足,而不得不终止用户临时不使用的程序时,究竟应该如何保证用户的体验?
比如,在编写上百字的长评论/小作文时,突然接收到推销电话,这时候切换出去,因为内存原因,系统不得不终止该程序,那么上百字的小作文何去何从?
当系统因为电量不足,程序意外关闭的场景时,这时候还没有发布的小作文该怎么办?
既然需要优化用户的体验,那么保存界面状态,就成为了一个值得探讨的话题。
什么时候保存界面状态?用什么保存界面状态?谁应该保存状态?
解决思路
什么时候保存界面状态
可以划分为两种场景。
用户发起的界面状态解除
- 从“概览”(“最近使用的应用”)屏幕中滑动关闭 activity。
- 主动退出(如用户按返回键)
- 从“设置”屏幕中终止或强制退出应用。
- 重新启动设备。
- 完成某种“完成”操作(由
Activity.finish()提供支持)。
针对以上这些状态,可以认为无需保存状态,或者数据需通过持久化存储管理。
系统发起的界面状态解除
- 配置更改(如屏幕旋转):系统重建
Activity,需临时保存数据以便快速恢复。 - 异常退出(如内存回收):系统终止进程,需临时保存数据或持久化关键数据。
解决方案
用什么保存界面状态?
临时保存方案
-
(1)
onSaveInstanceState与 Bundle-
作用:保存轻量级数据(如文本输入、控件状态)到内存,适用于异常退出或配置更改后的恢复。
-
实现:
override fun onSaveInstanceState(outState: Bundle) { outState.putString("text_key", textView.text.toString()) super.onSaveInstanceState(outState) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val savedText = savedInstanceState?.getString("text_key") textView.text = savedText } -
限制:仅适合小数据(1MB以内,可能由于设备不同有变化),复杂对象需序列化,由于
Binder的原因对性能有一定影响。 -
场景:适用于配置更改和系统发起的进程终止的情况。
-
-
(2)
ViewModel(onRetainNonConfigurationInstance方法的实现)- 作用:保留内存中的数据(如网络请求结果、复杂对象),避免重复加载。
- 优势:生命周期独立于 Activity,自动管理数据保留与清理。
- 实现:本质上是调用
onRetainNonConfigurationInstance方法,只在配置发生改变时被调用,但是对数据大小、类型都不做限制(如果配合SavedStateHandle则没有该特性),也是保存于内存中。 - 场景:仅适用于配置更改时,系统发起的进程终止依然会导致
ViewModel实例销毁
-
(3)
SavedStateHandle-
作用:将
ViewModel与onSaveInstanceState结合,通过键值对保存状态。 -
限制:本质上是通过
onSaveInstanceState与Bundle的实现,仅适合小数据(1MB以内,可能由于设备不同有变化),复杂对象需序列化,由于Binder的原因对性能有一定影响。 -
场景:适用于配置更改和系统发起的进程终止的情况。
-
持久化保存方案
- 本地数据库(Room):保存结构化数据(如用户设置、列表数据)。
SharedPreferences/ MMKV:存储简单键值对(如用户偏好)。- 文件存储:保存大型数据(如图片、文档)。
配置更改的特殊处理
android:configChanges- 作用:声明 Activity 自行处理配置更改(如屏幕旋转、屏幕尺寸发生变更),避免重建。
- 示例:
android:configChanges="orientation|screenSize" - 风险:需手动更新布局,可能引入兼容性问题,官方不推荐过度使用
谁应该保存界面状态?
缺乏统一的管理层
Activity、Fragment等不同组件均依赖不同的方法对状态进行保存,对同一类数据的保存方法复用性、可拓展性差,模板代码高重复。对于数据恢复的时机不定。
虽然后来引入了ViewModel,但是ViewModel只能处理配置变化的情况,对于系统意外杀死进程的情况,无可奈何。那么SavedStateHandle的推出,与ViewModel的配合,直接让:谁应该保存界面状态?这个问题,得到了回答。SavedStateHandle 将界面状态的保存职责集中到 ViewModel 中,而非分散在 Activity/Fragment 的 onSaveInstanceState()中。
总结
| ViewModel | 保存的实例状态 | 永久性存储空间 | |
|---|---|---|---|
| 存储位置 | 在内存中 | 在内存中 | 在磁盘或网络上 |
| 在配置更改后继续存在 | 是 | 是 | 是 |
| 在系统发起的进程终止后继续存在 | 否 | 是 | 是 |
| 在用户完全关闭 activity 或触发 onFinish() 后继续存在 | 否 | 否 | 是 |
| 数据限制 | 支持复杂对象,大小不受限制 | 仅适用于基元类型和简单的小对象,例如字符串 | 仅受限于磁盘空间或从网络资源检索的成本/时间 |
| 读取/写入时间 | 快(仅限内存访问) | 慢(需要序列化/反序列化) | 慢(需要磁盘访问或网络事务) |
注意:上表中的保存的实例状态是包括 onSaveInstanceState() 和 rememberSaveable API,以及 SavedStateHandle(作为 ViewModel 的一部分)。即如果ViewModel使用SavedStateHandle则归于保存的实例状态类。
https://developer.android.com/topic/libraries/architecture/saving-states?hl=zh-cn#options
明显的能够看见SavedStateHandle的出现,很好的处理了配置更改和系统发起的进程终止这两者情况,那么也正如标题一般,接下来的目标就是去了解SavedStateHandle这个组件。
核心类
| 类名 | 作用 |
|---|---|
SavedStateHandle |
核心类,封装了状态数据的保存与恢复逻辑,提供键值对存储,支持通过LiveData、StateFlow保存、获取状态。属于ViewModel级状态保存类。 |
SavedStateRegistry |
由Activity/Fragment持有,负责执行恢复、保存Bundle、打包所有注册的SavedStateProvider为Bundle的功能。属于系统级状态保存类。在辅助类 SavedStateHandleController中,ViewModel的SavedStateHandle#savedStateProvider会直接注册在SavedStateRegistry,而不是SavedStateHandlesProvider。 |
SavedStateProvider |
接口,作为状态提供者,定义saveState()方法:需要将数据保存到Bundle,然后返回这个Bundle。其实现类有 SavedStateHandlesProvider、Recreator.SavedStateProvider。 |
SavedStateRegistryOwner |
接口。由ComponentActivity/Fragment实现,要求能够提供SavedStateRegistry访问。 |
SavedStateRegistryController |
属于SavedStateRegistry的外观/包装类。通过订阅SavedStateRegistryOwner的生命周期事件,保证SavedStateRegistry的初始化performAttach、状态保存Save、恢复Restore方法在合适的Owenr的生命周期执行。同时初始化 Recreator,使得其能在onCreate时进行按照类名单重建实例。 |
SavedStateHandleController |
辅助类,创建时接受一个Key、SavedStateHandle。同时,实现 LifecycleEventObserver接口,接收SavedStateRegistryOwner生命周期的事件,从而保证内存不泄露。attachToLifecycle时将SavedStateHandle#savedStateProvider()注册到SavedStateRegistry。(不使用CreationExtras创建ViewModel创建,就会使用这个兼容类) |
LegacySavedStateHandleController |
工具类,辅助SavedStateHandleController的创建:使得attachToLifecycle,使得刚创建的SavedStateHanlde能够注册到Controller,再返回Controller给创建ViewModel的工厂Factory;顺便把OnRecreation加入重建名单。(不使用CreationExtras创建ViewModel创建,就会使用这个兼容类) |
SavedStateHandleSupport |
辅助类。通过enableSavedStateHandles启动。所有SavedStateHandle创建时都会保存到Owner#savedStateHandlesVM,可以通过Key获取复用。(注意savedStateHandlesVM虽然没有SavedStateHandle作为构造参数,但是在系统杀死进程后仍然可以重新恢复数据,见下) |
SavedStateHandlesProvider |
打包器。继承SavedStateProvider。保证savedStateHandlesVM被杀死后,能够恢复SavedStateHandle的类。其saveState()方法是调用savedStateHandlesVM中所有SavedStateProvider的saveState(),利用VIEW_MODEL_KEY打包成多个Bundle后放入自己的Bundle,以供下次恢复。 |
AutoRecreated |
接口。如果一个类需要通过Recreator实现重建,那么需要实现这个接口。 |
Recreator |
重建器。实现LifecycleEventObserver接口,能够在onCreate阶段时,通过获取SavedStateRegistry保存的Bundle,得到之前已注册的,需要重建的类名单,从而重建实例(要求有无参构造函数)。(即使使用CreationExtras,这个兼容类也会必然运行,算是默认开启的) |
OnRecreation |
辅助类,实现AutoRecreated接口,OnRecreation正是在Recreator的重建名单上。重建时,负责进行 attachToLifecycle,使得所有ViewModel的SavedStateHanlde能够再次注册到SavedStateHandleController上。同时调用 Registry.runOnNextRecreation继续把本身放入需要重建的类名单,以维持循环。(即使使用CreationExtras,这个兼容类也会必然运行,算是默认开启的) |
Recreator.SavedStateProvider |
打包器。实现SavedStateRegistry.SavedStateProvider,saveState()保存的是需要重建的类名单。(即使使用CreationExtras,这个兼容类也会必然运行,算是默认开启的) |
关键流程/时序图





注意
SavedStateProvider 接口的设计目的是什么?
集中式状态管理、生命周期感知、简化操作:
统一注册机制,所有SavedStateProvider 都可以注册到Registry中,方便管理:能在正确的生命周期时机被调用打包成Bundle保存,减少通过onSaveInstanceState()手动管理可能导致的错误。
SavedStateRegistry 的作用是什么?它是如何与 SavedStateHandle 关联的?
SavedStateRegistryController的作用是什么?它是如何协调 Activity 和 SavedStateRegistry 的?
SavedStateHandlesProvider的作用是什么?与SavedStateHandleProvider的关系?
SavedStateRegistry 的 performSave() 和 performRestore() 方法的调用时机是什么?
SavedStateRegistry 的 performSave() 方法为何要合并 restoredState 和当前状态?源码中如何实现覆盖逻辑?
问题一:
SavedStateRegistry 由Activity/Fragment持有,负责执行恢复、保存Bundle、打包所有注册的SavedStateProvider为Bundle的功能。属于系统级状态保存类。
SavedStateHandlesProvider注册在Registry,Registry执行performSave时会把注册的SavedStateProvider打包成Bundle;那么SavedStateHandlesProvider打包时会把所有ViewModel的SavedStateHandle#savedStateProvider分别打包成的Bundle放进来。
在辅助类SavedStateHandleController中,ViewModel的SavedStateHandle#savedStateProvider会直接注册在SavedStateRegistry,Registry执行performSave时会把注册的SavedStateProvider打包成Bundle,而不是通过SavedStateHandlesProvider打包成Bundle。
问题二:
SavedStateRegistryController属于SavedStateRegistry的外观/包装类,负责协调Activity/Fragment和Registry。通过订阅SavedStateRegistryOwner的生命周期事件,保证SavedStateRegistry的初始化performAttach、状态保存Save、恢复Restore方法在合适的Owenr的生命周期执行。
同时初始化Recreator,能够兼容旧方法,使得Recreator能在onCreate时进行按照类名单重建实例。
问题三:
SavedStateHandlesProvider作为打包器,继承SavedStateProvider。
保证savedStateHandlesVM被杀死后,能够恢复SavedStateHandle的类。
其saveState()方法是调用savedStateHandlesVM中所有SavedStateProvider的saveState(),利用VIEW_MODEL_KEY打包成多个Bundle后放入自己的Bundle,以供下次恢复。
class SavedStateRegistry internal constructor() {}
//SavedStateRegistry 通过internal修饰构造函数,只能被同一模块内的类调用
class SavedStateRegistryController private constructor(private val owner: SavedStateRegistryOwner) {
//因此只能通过SavedStateRegistryController,创建SavedStateRegistry对象
//每一个SavedStateRegistryController都绑定了SavedStateRegistry对象
//SavedStateRegistryController对象的创建,位于ComponentActivity/Fragment的构造函数中
val savedStateRegistry: SavedStateRegistry = SavedStateRegistry()
}
//AndroidX.Frgament
public class Fragment{
public Fragment() {
initLifecycle();
}
private void initLifecycle() {
mLifecycleRegistry = new LifecycleRegistry(this);
mSavedStateRegistryController = SavedStateRegistryController.create(this);
}
}
//接下来仅以ComponentActivity为例子
public class ComponentActivity{
//SavedStateRegistryController对象的创建,位于ComponentActivity中
final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
//构造函数
public ComponentActivity() {
//先执行performAttach
mSavedStateRegistryController.performAttach();
//后续会提
enableSavedStateHandles(this);
}
}
//mSavedStateRegistryController#performAttach()
fun performAttach() {
val lifecycle = owner.lifecycle
check(lifecycle.currentState == Lifecycle.State.INITIALIZED) {
("Restarter must be created only during owner's initialization stage")
}
//这里是默认开启Recreator,兼容旧版本ViewModel
lifecycle.addObserver(Recreator(owner))
//实际上是调用savedStateRegistry#performAttach
savedStateRegistry.performAttach(lifecycle)
attached = true
}
//savedStateRegistry#performAttach
@MainThread
internal fun performAttach(lifecycle: Lifecycle) {
check(!attached) { "SavedStateRegistry was already attached." }
lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
isAllowingSavingState = true
} else if (event == Lifecycle.Event.ON_STOP) {
isAllowingSavingState = false
}
})
attached = true
}
public class ComponentActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//onCreate时执行performRestore(savedInstanceState),把保存的bundle传入
mSavedStateRegistryController.performRestore(savedInstanceState);
//...
}
}
//直接来看savedStateRegistry.performRestore(bundle)
@MainThread
internal fun performRestore(savedState: Bundle?) {
check(attached) {
("You must call performAttach() before calling " +
"performRestore(Bundle).")
}
check(!isRestored) { "SavedStateRegistry was already restored." }
restoredState = savedState?.getBundle(SAVED_COMPONENTS_KEY)
isRestored = true
}
//ComponentActivity#onSaveInstanceState
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
Lifecycle lifecycle = getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
}
super.onSaveInstanceState(outState);
//Registry执行performSave的时机,也就是进行保存Bundle的时机
mSavedStateRegistryController.performSave(outState);
}
//Registry#performSave
//它将调用所有已注册的SavedStateProvider,并合并未使用的状态restoredState。
@MainThread
@Suppress("INACCESSIBLE_TYPE")
fun performSave(outBundle: Bundle) {
val components = Bundle()
if (restoredState != null) {
//通过 restoredState 恢复之前保存的状态。
//因为有可能用户刚执行完performRestore后,又划出使用其他程序
//刚恢复的状态需要被保存,以等待用户继续使用
components.putAll(restoredState)
}
val it: Iterator<Map.Entry<String, SavedStateProvider>> =
//解决迭代时,saveState()又创建了新的SavedStateProvider
this.components.iteratorWithAdditions()
while (it.hasNext()) {
val (key, value) = it.next()
components.putBundle(key, value.saveState())
}
if (!components.isEmpty) {
outBundle.putBundle(SAVED_COMPONENTS_KEY, components)
}
}
直到上述,SavedStateRegistry执行恢复、保存Bundle的功能已经结束了。
回到上面在ComponentActivity构造函数中没有讲解的enableSavedStateHandles方法。实际上就是SavedStateRegistry是打包所有注册的SavedStateProvider为Bundle的功能。
@MainThread
fun <T> T.enableSavedStateHandles()
where T : SavedStateRegistryOwner, T : ViewModelStoreOwner {
val currentState = lifecycle.currentState
require(
currentState == Lifecycle.State.INITIALIZED || currentState == Lifecycle.State.CREATED
)
//添加用于保存 所有Viewmodel的SavedStateHandle打包成的Bundle 的SavedStateHandlesProvider(如果尚未注册)
//这里就是savedStateRegistry 与 SavedStateHandle 的关联处
//所有SavedStateHandle分别打包成的Bundle 都会放入 SavedStateHandlesProvider的Bundle
if (savedStateRegistry.getSavedStateProvider(SAVED_STATE_KEY) == null) {
val provider = SavedStateHandlesProvider(savedStateRegistry, this)
savedStateRegistry.registerSavedStateProvider(SAVED_STATE_KEY, provider)
lifecycle.addObserver(SavedStateHandleAttacher(provider))
}
}
internal class SavedStateHandlesProvider(
private val savedStateRegistry: SavedStateRegistry,
viewModelStoreOwner: ViewModelStoreOwner
) : SavedStateRegistry.SavedStateProvider {
private var restored = false
private var restoredState: Bundle? = null
private val viewModel by lazy {
viewModelStoreOwner.savedStateHandlesVM
}
override fun saveState(): Bundle {
return Bundle().apply {
//确保即使在进程终止和重新创建后
//没有重新创建 ViewModel,我们也会保留其状态,直到重新创建它们
//因为可能出现刚onCreate,用户就又切出去,使得不得不再次performSave
if (restoredState != null) {
putAll(restoredState)
}
//但是如果有 ViewModel,
//那么ViewModel对应的SavedStateHandle打包成的Bundle,会被再次打包
//由于同一个Key,那么可能会覆盖掉刚刚put的restoredState
//key是创建ViewModel时来自CreationExtras中的VIEW_MODEL_KEY
//也就是重建后的ViewModel#SavedStateHandle Bundle优先级,大于重建前的
viewModel.handles.forEach { (key, handle) ->
val savedState = handle.savedStateProvider().saveState()
if (savedState != Bundle.EMPTY) {
putBundle(key, savedState)
}
}
}.also {
// After we've saved the state, allow restoring a second time
restored = false
}
}
}
SavedStateViewModelFactory 类在 SavedStateHandle 的创建过程中扮演什么角色?
ViewModel with SavedState的创建有两种方式。这里主要介绍的是流行的、新兴的方法,也就是第一种方法,通过CreationExtras创建。
需要明确SavedStateHandle一般是与ViewModel配合使用的,所以需要去SavedViewModelFactory中查看。
//第一种方法:
//SavedViewModelFactory#create(modelClass, CreationExtras)
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val key = extras[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY]
?: throw IllegalStateException(
"VIEW_MODEL_KEY must always be provided by ViewModelProvider"
)
return if (extras[SAVED_STATE_REGISTRY_OWNER_KEY] != null &&
extras[VIEW_MODEL_STORE_OWNER_KEY] != null) {
//...
val viewModel = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application,
//这里创建了SavedStateHandle
extras.createSavedStateHandle())
} else {
newInstance(modelClass, constructor,
//这里创建了SavedStateHandle
extras.createSavedStateHandle())
}
viewModel
} else {
//...
}
}
@MainThread
public fun CreationExtras.createSavedStateHandle(): SavedStateHandle {
//判断CreationExtras是否有savedStateRegistry的信息
val savedStateRegistryOwner = this[SAVED_STATE_REGISTRY_OWNER_KEY]
?: throw IllegalArgumentException(
"CreationExtras must have a value by `SAVED_STATE_REGISTRY_OWNER_KEY`"
)
val viewModelStateRegistryOwner = this[VIEW_MODEL_STORE_OWNER_KEY]
?: throw IllegalArgumentException(
"CreationExtras must have a value by `VIEW_MODEL_STORE_OWNER_KEY`"
)
val defaultArgs = this[DEFAULT_ARGS_KEY]
val key = this[VIEW_MODEL_KEY] ?: throw IllegalArgumentException(
"CreationExtras must have a value by `VIEW_MODEL_KEY`"
)
return createSavedStateHandle(
savedStateRegistryOwner, viewModelStateRegistryOwner, key, defaultArgs
)
}
private fun createSavedStateHandle(
savedStateRegistryOwner: SavedStateRegistryOwner,
viewModelStoreOwner: ViewModelStoreOwner,
key: String,
defaultArgs: Bundle?
): SavedStateHandle {
val provider = savedStateRegistryOwner.savedStateHandlesProvider
val viewModel = viewModelStoreOwner.savedStateHandlesVM
//所有通过CreationExtras创建SavedStateHandle的,都会存入viewModel.handles
//viewModel.handles 也就是最终被 SavedStateHandlesProvider打包成bundle的
//见SavedStateHandlesProvider#saveState (上面有源码)
return viewModel.handles[key] ?: SavedStateHandle被创建出来.createHandle(
provider.consumeRestoredStateForKey(key), defaultArgs
).also { viewModel.handles[key] = it }
}
//第2种,兼容旧版的方法,这里不做详细讨论。
//SavedViewModelFactory#create(key, modelClass)
//savedStateRegistry 和 SavedStateHandle.savedStateProvider()
fun <T : ViewModel> create(key: String, modelClass: Class<T>): T {
//...
//通过create创建SavedStateHandleController
//Legacy:遗产、遗留
val controller = LegacySavedStateHandleController.create(
savedStateRegistry!!, lifecycle, key, defaultArgs
)
val viewModel: T = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application!!, controller.handle)
} else {
newInstance(modelClass, constructor, controller.handle)
}
viewModel.addCloseable(
LegacySavedStateHandleController.TAG_SAVED_STATE_HANDLE_CONTROLLER, controller
)
return viewModel
}
//LegacySavedStateHandleController#create
//创建controller,需要registry来注册SavedStateHandle.savedStateProvider()
//controller再被addColosable进入Viewmodel
@JvmStatic
fun create(
registry: SavedStateRegistry,
lifecycle: Lifecycle,
key: String?,
defaultArgs: Bundle?
): SavedStateHandleController {
val restoredState = registry.consumeRestoredStateForKey(key!!)
val handle = createHandle(restoredState, defaultArgs)
val controller = SavedStateHandleController(key, handle)
//SavedStateHandleController#attachToLifecycle
controller.attachToLifecycle(registry, lifecycle)
tryToAddRecreator(registry, lifecycle)
return controller
}
//SavedStateHandleController#attachToLifecycle
fun attachToLifecycle(registry: SavedStateRegistry, lifecycle: Lifecycle) {
check(!isAttached) { "Already attached to lifecycleOwner" }
isAttached = true
lifecycle.addObserver(this)
//savedStateRegistry 和 SavedStateHandle.savedStateProvider() 直接关联
registry.registerSavedStateProvider(key, handle.savedStateProvider())
}
SavedStateHandleController 的作用是什么?它是如何协调 ViewModel 和 SavedStateRegistry 的?
兼容旧版的方法,这里不做详细讨论。上面一个问题有涉及。
SavedStateHandle 的核心作用是什么?适用场所是什么?
SavedStateHandle 内部如何存储数据?底层数据结构是什么?
它与 Bundle 的关系是什么?SavedStateHandle 的数据是如何序列化到 Bundle 中的?
SavedStateHandle 支持保存哪些数据类型?不支持的类型如何处理?
反序列化时如何处理类型安全?
SavedStateHanlde是如何保存Livedata、StateFlow的?
问题一
SavedStateHandle 的核心作用 是 简化界面状态在配置变化和进程终止时的保存与恢复流程。本质上是通过onSaveInstanceState方法的进行保存,所以仅适用于非用户主动关闭进程的情况。
问题五:
从get方法的注释就知道,get方法有些需要特别注意。
如果get的是Array<T extend Parcelable>,那么就需要使用Array<Parcelable>接收结果,同时对逐个元素进行转换。
主要原因是:
一、在Java数组支持协变(covariance),也就是Java中:Object[] 能被赋值为 String[];
Kotlin 中数组本身不支持协变。是由于数组Array<T>使用了泛型,其行为与集合类类似,不支持协变,本身是不变的(invariant)。(但是可以通过in、out、*实现协变、逆变)
Kotlin中Array<A> 不能被赋值为 Array<B>, 即使A 是 B 父类。
二、
当发送端和接收端在同一 Linux 进程中时,Bundle 会走快速通道,跳过真正的 Parcel 序列化/反序列化,直接复用原始对象引用。
// 放入
bundle.putParcelableArray("key", myArrayOfMyClass);
// 取出时拿到的是同一个对象
Parcelable[] p = bundle.getParcelableArray("key");
MyClass[] arr = (MyClass[]) p; // 安全!
当你的 Activity 或进程在后台被系统杀死时,会将 Bundle 强制通过 Binder 序列化,由于 Binder 的协议是不记录数组的具体组件类型,反序列化后数组的类型只能是通用的 Parcelable[],但是对元素的类型是有做记录的,元素本身的类型信息会被保留并写入 Parcel 流中,使得可以逐个转换元素类型。
在
Parcel的内部协议中,VAL_PARCELABLEARRAY(值 16)标记了一段长度前缀数据块,表明接下来是一组Parcelable对象;而VAL_OBJECTARRAY(值 17)则用于更通用的对象数组,但两者都只记录“这是一个Parcelable数组”或Object数组,并不区分MyClass[]、YourClass[]等具体组件类型。
//以Parcelable[]接收
Parcelable[] generic = bundle.getParcelableArray("key");
MyClass[] arr = new MyClass[generic.length];
for (int i = 0; i < generic.length; i++) {
arr[i] = (MyClass) generic[i]; //逐个转换
}
问题六:
不保存。只有regular是核心数据结构,只保存regular。
class SavedStateHandle {
//SavedStateHandle的核心数据结构——regular 是一个Map
private val regular = mutableMapOf<String, Any?>()
//负责保存注册在SavedStateHandle的SavedStateProvider
private val savedStateProviders = mutableMapOf<String, SavedStateRegistry.SavedStateProvider>()
//向外发布订阅的两种形式——livedata、stateflow
private val liveDatas = mutableMapOf<String, SavingStateLiveData<*>>()
private val flows = mutableMapOf<String, MutableStateFlow<Any?>>()
private val savedStateProvider =
SavedStateRegistry.SavedStateProvider {
// 获取所有 注册在SavedStateHandle的SavedStateProvider打包成的bundle, iterating through a copy to avoid re-entrance
//为什么使用Copy?
//例如,假设在遍历savedStateProviders时,某个Provider的saveState()方法可能会向savedStateProviders添加或删除另一个Provider。
//如果直接遍历原始的集合,这样的修改会导致当前的迭代器抛出异常,或者在遍历过程中出现不一致的状态。
//通过创建一个拷贝(如toMap()),可以确保遍历的是当前时刻的快照,即使原始集合在遍历过程中被修改,也不会影响当前的遍历过程。
val map = savedStateProviders.toMap()
for ((key, value) in map) {
val savedState = value.saveState()
set(key, savedState)
}
// Convert the Map of current values into a Bundle
//只保存了regular 以及 savedStateProviders打包成的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])
}
//SavedStateHandle与Bundle的关系体现在此,SavedStateHandle所有数据最终都会被序列化到bundle中
//因此,由于Bundle 的大小限制为1MB,SavedStateHandle需要尽可能的保存小对象、关键数据,否则在恢复数据时容易出现TransactionTooLargeException
//大于1MB的对象,如果需要复用,建议持久化保存到本地。
bundleOf(KEYS to keys, VALUES to value)
}
/**
* Creates a handle with the given initial arguments.
*
* @param initialState initial arguments for the SavedStateHandle
*/
constructor(initialState: Map<String, Any?>) {
regular.putAll(initialState)
}
@MainThread
operator fun <T> set(key: String, value: T?) {
//因为最后需要序列化进入Bundle,所以这里需要检验是否为基础类型、Parcelable的类
if (!validateValue(value)) {
throw IllegalArgumentException(
"Can't put value with type ${value!!::class.java} into saved state"
)
}
@Suppress("UNCHECKED_CAST")
//Java的特性,动态分派:
//类型转换仅改变引用的声明类型,不影响对象的实际类型。
//也就是调用mutableLiveData.setValue时,实际上调用的是SavingLivedata的setValue
//而非MutableLiveData.setValue
val mutableLiveData = liveDatas[key] as? MutableLiveData<T?>?
//由于Livedata需要set一次才能分发数据,所以和StateFlow略微不同
//如果mutableLiveData不为空,那么直接setValue到livedata
//SavingLivedata.setValue中会自动把数据push到regular
if (mutableLiveData != null) {
// it will set value;
mutableLiveData.setValue(value)
} else {
regular[key] = value
}
flows[key]?.value = value
}
//返回与给定键关联的值。
//注意:如果 [T] 是 [Parcelable] 类的 [Array],因为经历进程终止和重新创建(或使用“不保留活动”开发人员选项)将导致类型信息丢失,因此应该始终使用 'Array<Parcelable>'接收get的结果,如果直接尝试将结果分配给 'Array<T>' ,则会导致 'ClassCastException<CustomParcelable>'。
//val typedArray = savedStateHandle.get<Array<Parcelable>>(“KEY”).map { it as CustomParcelable }.toTypedArray
@MainThread
operator fun <T> get(key: String): T? {
return try {
@Suppress("UNCHECKED_CAST")
regular[key] as T?
} catch (e: ClassCastException) {
// Instead of failing on ClassCastException, we remove the value from the
// SavedStateHandle and return null.
remove<T>(key)
null
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun validateValue(value: Any?): Boolean {
if (value == null) {
return true
}
for (cl in ACCEPTABLE_CLASSES) {
if (cl!!.isInstance(value)) {
return true
}
}
return false
}
// doesn't have Integer, Long etc box types because they are "Serializable"
//装箱类型 Long、Integer 不在 ACCEPTABLE_CLASSES,但实际上其实现了 Serializable,会被后续的 Serializable::class.java 检查接受。
//SavedStateHandle 接受的类型,实际上就是Bunlde接受的类型
//如果Bundle不接受,那么可以考虑转为ByteArray、String。
//或者实现 Parcelable(推荐,性能更好)或 Serializable。
//Kotlin实现Parcelable十分方便,通过@Parcelize即可
//注意:ACCEPTABLE_CLASSES中并没有Livedata、StateFlow
//实际上是并没有保存,Livedata、StateFlow发布的数据实际上来自regular
//因此只需要保存regular即可
private val ACCEPTABLE_CLASSES = arrayOf( // baseBundle
Boolean::class.javaPrimitiveType,
BooleanArray::class.java,
Double::class.javaPrimitiveType,
DoubleArray::class.java,
Int::class.javaPrimitiveType,
IntArray::class.java,
//Long对应的原始类型long
Long::class.javaPrimitiveType,
LongArray::class.java,
String::class.java,
Array<String>::class.java, // bundle
Binder::class.java,
Bundle::class.java,
Byte::class.javaPrimitiveType,
ByteArray::class.java,
Char::class.javaPrimitiveType,
CharArray::class.java,
CharSequence::class.java,
Array<CharSequence>::class.java,
// type erasure ¯\_(ツ)_/¯, we won't eagerly check elements contents
ArrayList::class.java,
Float::class.javaPrimitiveType,
FloatArray::class.java,
Parcelable::class.java,
Array<Parcelable>::class.java,
Serializable::class.java,
Short::class.javaPrimitiveType,
ShortArray::class.java,
SparseArray::class.java,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
Size::class.java
else
Int::class.javaPrimitiveType,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
SizeF::class.java
else
Int::class.javaPrimitiveType
)
}
}
SavedStateHandle 如何恢复进程被杀死后的数据(恢复流程)?
当 Activity/Fragment 被系统销毁重建时,SavedStateHandle 如何保证数据能正常恢复?
//SavedViewModelFactory#create->
//CreationExtras#createSavedStateHandle->
//SavedStateHandleSupport#createSavedStateHandle->
//sSavedStateHandle#createHandle
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmStatic
@Suppress("DEPRECATION")
fun createHandle(restoredState: Bundle?, defaultState: Bundle?): SavedStateHandle {
//没有恢复的状态,说明是第一次创建
if (restoredState == null) {
return if (defaultState == null) {
// 没有恢复的状态,也没有默认的状态 -> empty SavedStateHandle
SavedStateHandle()
} else {
//有默认的状态
val state: MutableMap<String, Any?> = HashMap()
for (key in defaultState.keySet()) {
state[key] = defaultState[key]
}
SavedStateHandle(state)
}
}
//恢复状态时,我们使用恢复的状态作为重建SavedStateHandle的数据来源
//并忽略任何默认状态,从而确保我们与保存的状态完全相同。
restoredState.classLoader = SavedStateHandle::class.java.classLoader!!
//注意这里用ArrayList<*>接收
//由于getParcelableArrayList是一个泛型方法,需要指定T extend Parcelable。
//作为一个开源框架需要保证数据的可用性,后续操作的拓展性,所以只指定类型为T的上限Parcelable.
//同时为了方便后续转变类型,所以使用ArrayList<*>接收,如果使用ArrayList<Parcelable>接收,把keys[i] as String,编译器会提示This cast can never succeed,是因为编译器看到你要把一个 Parcelable 强转成 String(非 Parcelable),它知道两者在类型系统里是不可能发生成功的类型转换的
//*只读时可以作为Any?(视作上界类型),*只写时可以作为Nothing?
val keys: ArrayList<*>? =
restoredState.getParcelableArrayList<Parcelable>(KEYS)
val values: ArrayList<*>? = restoredState.getParcelableArrayList<Parcelable>(VALUES)
check(!(keys == null || values == null || keys.size != values.size)) {
"Invalid bundle passed as restored state"
}
val state = mutableMapOf<String, Any?>()
for (i in keys.indices) {
state[keys[i] as String] = values[i]
}
return SavedStateHandle(state)
}
//这里`Bundle`需要手动设置`classLoader`。
restoredState.classLoader = SavedStateHandle::class.java.classLoader!!
JVM判定两个class是否相同:
不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。
只有两者同时满足的情况下,JVM才认为这两个class是相同的。
就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。
不管是Java虚拟机,还是Android中的Dalvik/ART虚拟机,都是使用ClassLoader来将Class加载到内存。
Bundle 底层通过 Parcel 实现数据的序列化和反序列化。当 Bundle创建时,存储自定义对象(如 Parcelable 或 Serializable)时,系统需要知道如何把字节流还原为对象。这时,ClassLoader 的作用就是告诉系统:“该去哪里查找这些类的定义”。
在 Android 中,Bundle 本身在通过默认构造函数创建时,其内部的 mClassLoader 初始化为引导(Bootstrap)类加载器,因此无法加载应用中的自定义类;
而当通过 Intent 在 Activity 之间传递 Parcelable 对象时,框架会在取出 extras 之前,自动为该 Bundle 指定一个合适的应用类加载器(即 PathClassLoader),从而保证反序列化时能找到相应的 Class。
但在以下两种场景下,ClassLoader 信息不会被自动修正,需要手动调用 setClassLoader(...):
- 跨进程或 IPC(如通过
Messenger)传递Bundle时,系统在另一进程中反序列化,框架不会为你指定应用加载器则mClassLoader = null,导致CustomParcelable无法找到。 会出现ClassNotFoundException。 - Activity 重建(如内存回收后重启)时,系统只会将
Bundle原样保存并在恢复时读取,不会重设mClassLoader。如果你的onSaveInstanceState()后没有手动修正mClassLoader,会出现ClassNotFoundException。一般会指定保存时使用的ClassLoader。
当 ViewModel 被清除时,SavedStateHandle 的数据如何被回收?是否存在内存泄漏风险?
SavedStateHandle 只是一个普通对象,随着 ViewModel 在 onCleared() 后失去所有活跃引用,即可被 JVM 垃圾回收;
SavedStateHandle内部是使用Bundle实现,进行跨进程(重建进程)序列化时,使用的是深拷贝,因此:
- 无泄漏的典型场景
- 若
SavedStateHandle存储的是 基本类型(如Int、String)或 轻量级可序列化对象,则不会导致内存泄漏。
- 若
- 潜在泄漏场景
- 错误存储大对象:若存储大型不可序列化的对象(如 Bitmap),可能导致临时内存占用过高,但严格来说不算“泄漏”,因为数据最终会被 GC 回收。
- 隐式引用:如果通过
SavedStateHandle间接持有 Activity/Fragment 的引用(例如通过 Lambda 或匿名内部类),则可能导致内存泄漏。
用户发起finish/回退 Activity/Fragment 的行为,SavedStateHandle 中数据会被清除吗?
实际上SavedStateHandle实例不活跃时,都是直接被JVM回收的。
只不过根据情况判断是否保存其中数据、是否需要恢复。
因此,每一次SavedStateHandle实例的创建都是全新的。
当用户自主发起finish/回退 Activity/Fragment时,是不会执行onSaveInstanceState方法的。因此在onCreate、onRestoreInstanceState方法接收的Bundle参数为null,那么SavedStateRegistry在执行performRestore时,是无法恢复restoredState变量的,最终结果创建的SavedStateHandle是空的,没有数据的。
//SavedStateRegistry#performRestore
@MainThread
internal fun performRestore(savedState: Bundle?) {
...
restoredState = savedState?.getBundle(SAVED_COMPONENTS_KEY)
isRestored = true
}
//SavedStateHandlesProvider#performRestore
fun performRestore() {
if (!restored) {
//这里使用consumeRestoredStateForKey获得恢复后的状态
//但是savedStateRegistry中的restoredState为null
//那么这里newState结果也为null
//最后SavedStateHandlesProvider#restoredState为null
val newState = savedStateRegistry.consumeRestoredStateForKey(SAVED_STATE_KEY)
// 伪代码表示执行顺序
//val tempBundle = Bundle() // 新建空Bundle
//tempBundle.putAll(oldRestoredState) // 合并旧状态(如果有)
//tempBundle.putAll(newState) // 合并新状态(如果有)
//restoredState = tempBundle // 最终赋值给类的属性
restoredState = Bundle().apply {
//配置变更时,保存旧状态
restoredState?.let { putAll(it) }
newState?.let { putAll(it) }
}
restored = true
viewModel
}
}
//SavedStateHandleSupport#createSavedStateHandle
private fun createSavedStateHandle(
savedStateRegistryOwner: SavedStateRegistryOwner,
viewModelStoreOwner: ViewModelStoreOwner,
key: String,
defaultArgs: Bundle?
): SavedStateHandle {
val provider = savedStateRegistryOwner.savedStateHandlesProvider
val viewModel = viewModelStoreOwner.savedStateHandlesVM
return viewModel.handles[key] ?: SavedStateHandle.createHandle(
//这里provider.consumeRestoredStateForKey(key)返回空,说明没有要恢复的状态,因此创建的是一个全新的SavedStateHandle
provider.consumeRestoredStateForKey(key), defaultArgs
).also { viewModel.handles[key] = it }
}
SavedStateHandle 与 onSaveInstanceState 的差异是什么?
本质SavedStateHandle 调用onSaveInstanceState 方法。
一个全自动,一个手动。
如果一个 Fragment 被替换(replace),其使用的 ViewModel的SavedStateHandle 数据会Save吗?为什么?
未加入回退栈:调用 replace() 后不调用 addToBackStack() 时,当前Fragment 直接被移除,其 onSaveInstanceState() 不会被系统调用,导致 SavedStateHandle 数据无法保存 。
加入回退栈:若调用 replace() 同时调用 addToBackStack(),则 当前Fragment 会被保留在回退栈中,onSaveInstanceState() 调用,从而保存其 SavedStateHandle 数据。
CreationExtras 是如何简化 SavedStateHandle(ViewModel)的创建的?
SavedStateHandle创建/重建时,需要尝试获取恢复状态restoreState,restoreState只能从Registry来,所以需要获得对应的Registry引用。
同时,创建后需要保存进ViewModelStoreOwner的savedStateHandlesVM中,这样才能在performSave时,保存自己的数据为Bundle,否则无法恢复。
那么SavedStateHandle的创建就需要,Registry、ViewModelStoreOwner的引用,CreationExtras通过KV键值对的方式,存储了这两者的引用,免去了使用其他方式时的初始化绑引用、重建再绑定引用流程。
SavedStateHandle 在 Jetpack Compose 中的 ViewModel 内使用时,源码流程有何不同?
//TODO:对比 viewModel() 函数与 SavedStateHandle 的集成
SavedStateHandle 与 Jetpack Navigation 的 Safe Args 如何配合使用?
//TODO:参数传递与状态恢复的协同
SavedStateHandle 在 Jetpack Compose 中的使用有何注意事项?
//TODO:与 rememberSaveable 的协作关系
补充
onSaveInstanceState、onRestoreInstanceState调用时机
以Android X作为背景。
首先需要明确,onSaveInstanceState、onRestoreInstanceState并非生命周期方法,所以调用时机不确定,只能依照情况进行分析。
当用户明确关闭活动或在其他情况下 finish()被调用时,onSaveInstanceState()不会被调用。在以下情况,onSaveInstanceState()可能会被调用:
-
当用户按下HOME键/划出应用时。
系统不知道你按下HOME后要运行多少其他的程序,也不知道Activity接下来是否会被销毁,因此系统会调用
onSaveInstanceState()。 -
长按HOME键/划出应用,选择运行其他的程序时。
-
按下电源按键(关闭屏幕显示)时。
-
从
Activity A中启动一个新的Activity B时。当调用
startActivity(intent)时,Activity A会依次触发onPause()、onStop()、onSaveInstanceState(Bundle)(如果系统预计 A 可能被销毁)回调。当用户从Activity B导航回Activity A时:B不会调用onSaveInstanceState,因为该特定实例永远不会恢复,因此系统会避免调用它。 -
屏幕方向切换时,例如从竖屏切换到横屏时。
对于onRestoreInstanceState,它只有在上次Activity被销毁后,在重建时onStart()后才会调用,所以其Bundle参数一定不会是空值。
如果使用onCreate()的Bundle参数,来恢复数据,一定要做非空判断。
https://developer.android.com/guide/components/activities/activity-lifecycle
Bundle序列化保存原理
//TODO
参考
https://juejin.cn/post/7299364292473192458
- 发现问题
- 解决思路
- 解决方案
- 注意
- SavedStateProvider 接口的设计目的是什么?
- SavedStateRegistry 的作用是什么?它是如何与 SavedStateHandle 关联的?
- SavedStateRegistryController的作用是什么?它是如何协调 Activity 和 SavedStateRegistry 的?
- SavedStateHandlesProvider的作用是什么?与SavedStateHandleProvider的关系?
- SavedStateRegistry 的 performSave() 和 performRestore() 方法的调用时机是什么?
- SavedStateRegistry 的 performSave() 方法为何要合并 restoredState 和当前状态?源码中如何实现覆盖逻辑?
- SavedStateViewModelFactory 类在 SavedStateHandle 的创建过程中扮演什么角色?
- SavedStateHandleController 的作用是什么?它是如何协调 ViewModel 和 SavedStateRegistry 的?
- SavedStateHandle 的核心作用是什么?适用场所是什么?
- SavedStateHandle 内部如何存储数据?底层数据结构是什么?
- 它与 Bundle 的关系是什么?SavedStateHandle 的数据是如何序列化到 Bundle 中的?
- SavedStateHandle 支持保存哪些数据类型?不支持的类型如何处理?
- 反序列化时如何处理类型安全?
- SavedStateHanlde是如何保存Livedata、StateFlow的?
- SavedStateHandle 如何恢复进程被杀死后的数据(恢复流程)?
- 当 Activity/Fragment 被系统销毁重建时,SavedStateHandle 如何保证数据能正常恢复?
- 当 ViewModel 被清除时,SavedStateHandle 的数据如何被回收?是否存在内存泄漏风险?
- 用户发起finish/回退 Activity/Fragment 的行为,SavedStateHandle 中数据会被清除吗?
- SavedStateHandle 与 onSaveInstanceState 的差异是什么?
- 如果一个 Fragment 被替换(replace),其使用的 ViewModel的SavedStateHandle 数据会Save吗?为什么?
- CreationExtras 是如何简化 SavedStateHandle(ViewModel)的创建的?
- SavedStateHandle 在 Jetpack Compose 中的 ViewModel 内使用时,源码流程有何不同?
- SavedStateHandle 与 Jetpack Navigation 的 Safe Args 如何配合使用?
- SavedStateHandle 在 Jetpack Compose 中的使用有何注意事项?
- 补充
- 参考