第2章 onSaveInstanceState与onRestoreInstanceState生命周期回调
Android 系统在 Activity 可能被销毁前(如旋转屏幕、内存不足、用户主动切换语言等),会回调 onSaveInstanceState(Bundle outState) 让你保存临时状态;重建后通过 onRestoreInstanceState(Bundle savedInstanceState) 或 onCreate(Bundle savedInstanceState) 恢复。理解这两个回调的调用时机、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 调试技巧:验证保存恢复是否生效
- 打开 “开发者选项” → “不保留活动”(在后台销毁 Activity)以模拟系统杀进程。
- 将应用切到后台,再重新打开(或旋转屏幕),观察状态是否恢复。
- 在
onSaveInstanceState与onRestoreInstanceState中打印日志:
Log.d("Lifecycle", "onSaveInstanceState: " + outState); Log.d("Lifecycle", "onRestoreInstanceState: " + savedInstanceState); - 使用 Profiler 或
adb shell dumpsys activity activities检查 Activity 的 lastSaveInstanceState 是否包含期望数据。