第2章 onSaveInstanceState与onRestoreInstanceState生命周期回调

Android 系统在 Activity 可能被销毁前(如旋转屏幕、内存不足、用户主动切换语言等),会回调 onSaveInstanceState(Bundle outState) 让你保存临时状态;重建后通过 onRestoreInstanceState(Bundle savedInstanceState) 或 onCreate(Bundle savedInstanceState) 恢复。理解这两个回调的调用时机、Bundle 传递机制以及常见误区,是编写健壮导航状态保存代码的基石。

onSaveInstanceState / onRestoreInstanceState 流程 Activity 正常运行 系统准备销毁 Activity (onPause → onStop → onDestroy) onSaveInstanceState 写入 Bundle Bundle 实例 key-value 数据 序列化持久化 Activity 重建 (新实例) onRestoreInstanceState 读取 Bundle 恢复状态 注意:onCreate 中也 可拿到 Bundle(非空判断)

2.1 调用时机与 Bundle 传递

回调方法调用时机Bundle 状态典型用途
onSaveInstanceState(Bundle)在 onStop 之前(可能早于 onPause 之后)被称为“预销毁保存”。outState 非空,可写入任意可序列化数据。保存控件状态、导航位置、用户输入。
onRestoreInstanceState(Bundle)在 onStart 之后、onResume 之前;只有重建时才会调用。savedInstanceState 非空(若第一次创建或未保存则为 null)。恢复 UI 状态(如 ViewPager 当前位置)。
onCreate(Bundle)Activity 生命周期入口,可能携带上次保存的 Bundle。首次创建为 null;重建时非空。根据 Bundle 决定初始化分支。

关键点:onSaveInstanceState 仅在 Activity 可能被杀死且不是用户主动 finish 时调用。按 Home 键(不杀进程)不一定会触发,但系统可能随时杀进程,因此最好在 onPause 之前保存所有重要临时数据。

2.2 代码示例:保存与恢复导航状态

以下示例演示如何保存当前选中的 Tab 索引以及 EditText 的输入内容:

public class NavigationActivity extends AppCompatActivity {
    private static final String KEY_SELECTED_TAB = "selected_tab";
    private static final String KEY_EDIT_TEXT = "edit_text";
    private int selectedTabIndex;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navigation);

        editText = findViewById(R.id.et_input);

        if (savedInstanceState != null) {
            // 从 Bundle 恢复状态
            selectedTabIndex = savedInstanceState.getInt(KEY_SELECTED_TAB, 0);
            String savedText = savedInstanceState.getString(KEY_EDIT_TEXT, "");
            editText.setText(savedText);
            // 恢复 Tab 选中
            TabLayout tabLayout = findViewById(R.id.tabs);
            TabLayout.Tab tab = tabLayout.getTabAt(selectedTabIndex);
            if (tab != null) tab.select();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // 保存当前状态
        outState.putInt(KEY_SELECTED_TAB, selectedTabIndex);
        outState.putString(KEY_EDIT_TEXT, editText.getText().toString());
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // 此处也可恢复(与 onCreate 相比,调用的时机在 onStart 之后)
        // 注:如果同时在 onCreate 和 onRestoreInstanceState 恢复,后者会覆盖前者
    }
}

建议:对简单 UI 状态(如 CheckBox 选中、ScrollView 位置),系统自动保存/恢复(需 freezeText 或 saveEnabled 为 true)。对于自定义状态,推荐在 onSaveInstanceState 中写入,并在 onCreate 中读取,因为 onRestoreInstanceState 可能在 onStart 后才调用,此时一些视图尚未完全初始化。

2.3 典型误区与避免指南

  • 误区一:在 onPause 中保存一切
    onPause 后 Activity 可能仍然存活(如进入另一个 Activity 但未销毁),此时保存是冗余的。onSaveInstanceState 才是专为可能销毁的场景设计。
  • 误区二:依赖 onRestoreInstanceState 的唯一性
    如果 Activity 是第一次创建(非重建),savedInstanceState 为 null,此时 onRestoreInstanceState 不会被调用。必须同时在 onCreate 中做 null 判断处理。
  • 误区三:超出 Bundle 大小限制
    Binder 事务缓冲区默认约 1MB(不同厂商有差异)。保存大对象(如 Bitmap、长列表)会导致 TransactionTooLargeException。应使用持久化存储(Room、SharedPreferences)或 ViewModel 代替。
  • 误区四:忽略系统自动保存的 View 层级
    系统会为拥有 ID 且设置了 android:saveEnabled="true" 的 View 自动保存/恢复。但如果你在 onCreate 中重新绑定数据后,系统自动恢复的数据可能覆盖你写入的数据。解决:在 onCreate 中判断 savedInstanceState == null 时才执行默认初始化。
  • 误区五:认为旋转屏幕一定会触发保存
    若在 Manifest 中设置 android:configChanges="orientation|screenSize" 且 onConfigurationChanged 被处理,则 Activity 不会重建,也不会走保存/恢复流程。此时需手动在 onConfigurationChanged 中维护状态。

性能警告:不要在 onSaveInstanceState 中执行耗时操作(如网络请求、数据库写入)。该回调运行在主线程,且必须在几十毫秒内完成,否则可能导致 ANR。建议仅保存内存中的轻量数据。

2.4 调试技巧:验证保存恢复是否生效

  1. 打开 “开发者选项” → “不保留活动”(在后台销毁 Activity)以模拟系统杀进程。
  2. 将应用切到后台,再重新打开(或旋转屏幕),观察状态是否恢复。
  3. 在 onSaveInstanceState 与 onRestoreInstanceState 中打印日志:
    Log.d("Lifecycle", "onSaveInstanceState: " + outState);
    Log.d("Lifecycle", "onRestoreInstanceState: " + savedInstanceState);
  4. 使用 Profiler 或 adb shell dumpsys activity activities 检查 Activity 的 lastSaveInstanceState 是否包含期望数据。