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(...):

  1. 跨进程或 IPC(如通过 Messenger)传递 Bundle 时,系统在另一进程中反序列化,框架不会为你指定应用加载器则mClassLoader = null,导致 CustomParcelable 无法找到。 会出现 ClassNotFoundException。
  2. 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()可能会被调用:

  1. 当用户按下HOME键/划出应用时。

    系统不知道你按下HOME后要运行多少其他的程序,也不知道Activity接下来是否会被销毁,因此系统会调用onSaveInstanceState()。

  2. 长按HOME键/划出应用,选择运行其他的程序时。

  3. 按下电源按键(关闭屏幕显示)时。

  4. 从Activity A中启动一个新的Activity B时。

    当调用 startActivity(intent) 时,Activity A 会依次触发 onPause()、 onStop() 、onSaveInstanceState(Bundle)(如果系统预计 A 可能被销毁)回调。当用户从Activity B 导航回Activity A 时:B不会调用onSaveInstanceState,因为该特定实例永远不会恢复,因此系统会避免调用它。

  5. 屏幕方向切换时,例如从竖屏切换到横屏时。

对于onRestoreInstanceState,它只有在上次Activity被销毁后,在重建时onStart()后才会调用,所以其Bundle参数一定不会是空值。

如果使用onCreate()的Bundle参数,来恢复数据,一定要做非空判断。

https://developer.android.com/guide/components/activities/activity-lifecycle

Bundle序列化保存原理

//TODO

参考

https://juejin.cn/post/7299364292473192458