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, charputInt(), putLong()…getInt(), getLong()…
包装类型 & StringString, 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 存储与恢复完整路径

onSaveInstanceState Bundle 键值对存储与恢复流程 Activity 运行中 系统销毁 Activity(旋转/回收) onSaveInstanceState 回调 创建 Bundle 并填充键值对 putInt / putString / putParcelable… 系统保存 Bundle(Binder) Activity 重建(获取 Bundle) onCreate 中读取 Bundle onRestoreInstanceState 恢复 ⚠ Bundle 大小建议 ≤ 500 KB,超限可能导致 TransactionTooLargeException

2.7 性能优化与最佳实践

  • 按需保存 —— 只保存重建时必须恢复的字段,避免保存可重新计算的派生数据。
  • 大对象使用 ViewModel + SavedStateHandle —— SavedStateHandle 内部自动处理序列化与大小限制,且支持更丰富的类型。
  • 测试边界条件 —— 使用开发者选项“不保留活动”或命令行 adb shell am start --activity-recreate 强制重建,验证数据完整。
  • 避免在 Bundle 中存放监听器、Handler、Activity 引用 —— 会导致内存泄漏或序列化失败。

掌握 Bundle 键值对存储的核心用法,是构建健壮导航状态保持的基础。下一环节将深入 SavedStateHandle 与 ViewModel 的协同机制(第3章)。