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 的生命周期绑定原理:
- Activity 创建时,
SavedStateRegistryController创建SavedStateRegistry。 ViewModelProvider调用工厂的create(Class, CreationExtras)方法,从CreationExtras中提取SAVED_STATE_REGISTRY_OWNER_KEY关联的 Registry。- Factory 从 Registry 中获取之前保存的 Bundle(通过
consumeRestoredStateForKey()),构造SavedStateHandle并注入 ViewModel 构造函数。 - 当 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. 状态流转示意图
9. 总结与部署注意
SavedStateHandle 是 ViewModel 持久化轻量数据的首选方案。部署时务必:
- 使用
SavedStateViewModelFactory或AbstractSavedStateViewModelFactory创建 ViewModel。 - 仅保存 Bundle 支持的类型,避免序列化异常。
- 对超过 1MB 的数据(如长字符串、大列表)进行分块或改用持久化存储。
- 在 ViewModel 的
init块中初始化所有需要恢复的键,确保重建后数据立即可用。