2. onSaveInstanceState核心用法
2.1 原理与回调时机
当 Activity 可能被系统销毁(如旋转屏幕、后台进程被回收)时,Android 框架会在 onStop() 之前调用 onSaveInstanceState(Bundle outState)。开发者需要在此方法中将临时状态以键值对形式写入 outState 对象。重建时,新的 Activity 实例会通过 onCreate(Bundle savedInstanceState) 或 onRestoreInstanceState(Bundle savedInstanceState) 接收同一个 Bundle。
关键原则:onSaveInstanceState 只保存“瞬间快照”,不适合持久化大对象或敏感数据。默认 super 实现已保存控件状态(如 EditText 文字),前提是 View 拥有 ID。
2.2 支持的数据类型
Bundle 本质是 Map<String, Object>,但只能存放特定类型。框架内部通过 Parcel 序列化,因此类型必须实现 Parcelable 或 Serializable(推荐 Parcelable)。
| 类别 | 具体数据类型 | put 方法 | get 方法 |
|---|---|---|---|
| 原生类型 | int, long, float, double, boolean, char | putInt(), putLong()… | getInt(), getLong()… |
| 包装类型 & String | String, CharSequence, Integer… | putString(), putCharSequence(), putSerializable() | getString(), getCharSequence()… |
| 数组 | int[], String[], byte[]… | putIntArray(), putStringArray() | getIntArray(), getStringArray() |
| Parcelable | 自定义Parcelable对象, Bitmap, Uri… | putParcelable(), putParcelableArrayList() | getParcelable(), getParcelableArrayList() |
| Serializable | 任何实现Serializable的对象 | putSerializable() | getSerializable() |
| 其他 | Bundle自身, Size, SizeF… | putBundle(), putSize() | getBundle(), getSize() |
2.3 大小限制与性能风险
Android 官方未公布硬上限,但实际传输受 Binder 事务缓冲区限制(约 1 MB)。由于 onSaveInstanceState 的 Bundle 会通过 Binder 传递给系统进程(AMS),建议单次保存数据 不超过 500 KB。超过时可能导致 TransactionTooLargeException 或系统静默丢弃数据。
典型陷阱:大 Bitmap 或长列表直接放入 Bundle 极易超出限制。应改用持久化方案(Room / DataStore)或压缩为 byte[] 后分块保存。
2.4 代码实现流程
标准存储与恢复步骤(以计数器为例):
public class MainActivity extends AppCompatActivity {
private static final String KEY_COUNT = "count";
private int count = 0;
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); // 必须调用,保存控件状态
outState.putInt(KEY_COUNT, count); // 写入自定义数据
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
count = savedInstanceState.getInt(KEY_COUNT, 0); // 恢复
}
}
}
2.5 常见错误与调试
- 遗忘调用 super.onSaveInstanceState(outState) — 导致所有 View 状态丢失,EditText、CheckBox 等控件无法自动恢复。
- 键值冲突 — 使用简单键如
"key"容易与第三方库或父类保留键冲突。建议加static final前缀(如类名+字段名)。 - 类型不匹配 — 存储时用 putInt,恢复时用 getString 会返回默认值且无编译期检查。需保证键-类型一致。
- 保存大数据不检查大小 — 引发 TransactionTooLargeException。调试时可临时抓取日志:
adb logcat | grep TransactionTooLargeException。 - 误用 Serializable 对象 — Serialization 反射开销大,应优先使用 Parcelable 或基本类型。
调试技巧:在 onSaveInstanceState 末尾添加日志打印 Bundle 大小(outState.size() 及估算字节数),提前拦截异常。
2.6 内联流程图:Bundle 存储与恢复完整路径
2.7 性能优化与最佳实践
- 按需保存 —— 只保存重建时必须恢复的字段,避免保存可重新计算的派生数据。
- 大对象使用 ViewModel + SavedStateHandle —— SavedStateHandle 内部自动处理序列化与大小限制,且支持更丰富的类型。
- 测试边界条件 —— 使用开发者选项“不保留活动”或命令行
adb shell am start --activity-recreate强制重建,验证数据完整。 - 避免在 Bundle 中存放监听器、Handler、Activity 引用 —— 会导致内存泄漏或序列化失败。
掌握 Bundle 键值对存储的核心用法,是构建健壮导航状态保持的基础。下一环节将深入 SavedStateHandle 与 ViewModel 的协同机制(第3章)。