SavedStateHandle 原理与使用

1. 设计动机与核心原理

SavedStateHandle 是 Jetpack ViewModel 在遇到进程终止或配置变更(屏幕旋转)时,自动保存与恢复轻量级数据的机制。它通过底层 SavedStateRegistry 实现数据序列化,将键值对存入 Bundle,并在 ViewModel 重建后自动还原。

核心工作流程:

  • 保存触发:Activity/Fragment 的 onSaveInstanceState 被调用,SavedStateRegistry 将当前所有注册的 SavedStateProvider 保存的数据写入 Bundle。
  • 恢复触发:Activity/Fragment 重建时,构造函数传入的 SavedStateHandle 自动从 SavedStateRegistry 中读取上次保存的 Bundle 数据。
  • 数据隔离:每个 ViewModel 实例对应独立的 SavedStateHandle,互不干扰。
核心优势:开发者无需手动处理 onSaveInstanceState / onRestoreInstanceState,SavedStateHandle 自动绑定 ViewModel 的生命周期。

2. SavedStateHandle 初始化

SavedStateHandle 的初始化由 ViewModel 的构造过程承担。系统通过 SavedStateViewModelFactory 或 AbstractSavedStateViewModelFactory 自动注入 SavedStateHandle。

// 默认工厂方式(Android 官方推荐)
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    // 构造函数中自动注入 SavedStateHandle
}

// 在 Activity 中使用
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// 内部自动创建 SavedStateHandle 并传递给 MyViewModel
注意:若自定义 ViewModelProvider.Factory,必须继承 AbstractSavedStateViewModelFactory 才能正确注入 SavedStateHandle。

3. 保存与恢复状态

SavedStateHandle 提供类似 Map 的 API:get()、set()、getLiveData()。所有写入的值会在 Activity 销毁时被自动序列化到 Bundle 并恢复。

// 保存
savedStateHandle["userName"] = "Alice"

// 恢复(在 ViewModel 初始化时调用)
val userName: String? = savedStateHandle["userName"]

// 使用 LiveData 监听(自动保持最新)
val userNameLiveData: MutableLiveData<String> = savedStateHandle.getLiveData("userName")
userNameLiveData.observe(owner) { name -> /* 自动接收恢复的值 */ }
方法 说明 支持序列化
get(String key)返回保存的值(泛型)是
set(String key, T value)存入新值是
getLiveData(String key)返回 LiveData 包装,同步数据变化是
keys()获取所有 key 集合—

4. Bundle 支持与数据类型限制

SavedStateHandle 内部使用 Bundle 作为持久化容器。只有 Android Bundle 支持的数据类型才能被自动保存与恢复。常见可用类型:

  • 基本类型及其数组:int, long, float, double, boolean, String
  • Serializable 实现类
  • Parcelable 实现类
  • Bundle 本身
  • ArrayList 和数组(需为 Serializable/Parcelable)
  • 其他系统支持的 Bundle 类型
常见错误:存入自定义非序列化对象(如 Room 实体、未实现 Parcelable 的数据类)会抛出 ClassCastException 或数据丢失。必须使用 Parcelable / Serializable 或转为基本类型存储。

5. 与 ViewModel 的关联机制

SavedStateHandle 与 ViewModel 的生命周期绑定原理:

  1. Activity 创建时,SavedStateRegistryController 创建 SavedStateRegistry。
  2. ViewModelProvider 调用工厂的 create(Class, CreationExtras) 方法,从 CreationExtras 中提取 SAVED_STATE_REGISTRY_OWNER_KEY 关联的 Registry。
  3. Factory 从 Registry 中获取之前保存的 Bundle(通过 consumeRestoredStateForKey()),构造 SavedStateHandle 并注入 ViewModel 构造函数。
  4. 当 Activity 进入 onSaveInstanceState 时,SavedStateRegistry 遍历所有 SavedStateProvider,收集它们的 Bundle 数据并最终写入 Activity 的 Bundle。
关键点:SavedStateHandle 的生命周期实际上就是 ViewModel 的生命周期(在 ViewModelStore 中被持有),但数据持久化依靠 Activity/Fragment 的 SavedStateRegistry。

6. 调试与验证

可通过以下方式验证数据是否正确保存和恢复:

  • 日志:在 ViewModel 的 init 块中打印 savedStateHandle 中的所有键值。
  • 开发者工具:使用 Android Studio 的 “App Inspection → Saved State” 面板查看当前保存的 Bundle 内容。
  • 模拟进程终止:在开发者选项中开启 “Don‘t keep activities”,旋转屏幕或进入后台后强制终止进程。
// 调试示例
init {
    savedStateHandle.keys().forEach { key ->
        Log.d("SavedStateDebug", "Key=$key, Value=${savedStateHandle.get(key)}")
    }
}

7. 常见错误与性能优化

问题现象解决方案
数据未恢复Activity 重建后 ViewModel 中的值为 null检查 ViewModel 是否使用默认工厂;确认数据类型 Bundle 支持
SavedStateHandle 未注入ViewModel 构造函数中的参数报错使用 AbstractSavedStateViewModelFactory 或确保 ViewModel 构造函数第一个参数为 SavedStateHandle
Parcelable 对象过大TransactionTooLargeException限制单个保存数据量 < 1MB;使用 Room 持久化大型数据
跨进程共享数据无法跨 Activity 或其他进程SavedStateHandle 仅用于当前 Activity/Fragment 的重建,非跨进程方案
性能优化建议:避免在 SavedStateHandle 中存储大型 Bitmap 或复杂 List。若业务数据超过 500KB,应考虑 Room 或 DataStore 持久化,SavedStateHandle 只保存关键索引或标识。

8. 状态流转示意图

SavedStateHandle 状态保存与恢复流程 Activity 重建 ViewModelProvider SavedStateRegistry consumeRestoredStateForKey() 创建 SavedStateHandle ViewModel 使用数据 set/get/getLiveData onSaveInstanceState Bundle 写入/读取 注意 仅保存轻量数据(<1MB) 复杂数据请用 Room/DataStore

9. 总结与部署注意

SavedStateHandle 是 ViewModel 持久化轻量数据的首选方案。部署时务必:

  • 使用 SavedStateViewModelFactory 或 AbstractSavedStateViewModelFactory 创建 ViewModel。
  • 仅保存 Bundle 支持的类型,避免序列化异常。
  • 对超过 1MB 的数据(如长字符串、大列表)进行分块或改用持久化存储。
  • 在 ViewModel 的 init 块中初始化所有需要恢复的键,确保重建后数据立即可用。