Activity生命周期与重建触发场景
Android Activity 从启动到销毁经历一组可预测的回调序列,称为“生命周期”。当系统资源不足或配置发生变更时,Activity 可能被重建,此时保存与恢复瞬时状态成为稳定性的关键。本章梳理生命周期回调的完整时序,深入分析进程被系统杀死、配置变更这两种典型重建场景,并明确 onSaveInstanceState 与 onRestoreInstanceState 的调用时机与回调顺序。
1.1 标准生命周期回调序列
一个 Activity 从创建到可见、可交互、被遮断、停止、销毁,共涉及 7 个核心回调。下表列出其典型调用顺序与常用职责:
| 回调方法 | 调用时机 | 典型职责 |
|---|---|---|
onCreate() |
Activity 首次创建时 | 初始化布局、绑定数据、恢复已保存的状态 |
onStart() |
Activity 即将对用户可见 | 注册广播/传感器、启动动画 |
onResume() |
Activity 获得用户焦点(可交互) | 启动相机、获取独占资源 |
onPause() |
Activity 失去焦点但仍可见(如弹窗) | 暂停动画、释放轻量资源 |
onStop() |
Activity 完全不可见 | 停止后台线程、关闭数据库连接 |
onDestroy() |
Activity 被销毁前 | 彻底清理资源、取消注册 |
onRestart() |
Activity 从停止状态重新可见前 | 重新获取之前释放的资源(非重建场景) |
1.2 重建触发场景对比
Activity 重建指“旧实例被销毁,新实例被创建”的过程。Android 中触发重建的主要原因有两个:
- 进程被系统杀死:后台 Activity 所在进程因内存不足被 LMK(Low Memory Killer)杀死,用户返回时系统重建 Activity。
- 配置变更:设备旋转、语言切换、屏幕尺寸变化等
Configuration改变,默认导致 Activity 销毁并重建。
关键差异:配置变更时系统会立即调用 onSaveInstanceState,而进程被杀死的场景下 onSaveInstanceState 也在 onStop 之前被调用,但保存的 Bundle 是否被实际传递到重建后的 onCreate 取决于进程是否真正死亡。配置变更的 Bundle 总是有效;进程被杀死后 Bundle 可能丢失(当系统再次需要内存时)。
1.3 onSaveInstanceState 与 onRestoreInstanceState 调用时机
这两个方法是专门为状态保存与恢复设计的回调,其调用时序如下:
- onSaveInstanceState(Bundle outState):在 Activity 可能被销毁之前调用,具体位于
onPause()之后、onStop()之前。系统保证该方法会在配置变更和进程被杀死(非用户主动关闭)时调用。 - onRestoreInstanceState(Bundle savedInstanceState):在
onStart()之后、onResume()之前调用。当 Activity 重建且保存的 Bundle 非空时才会触发。如果 Activity 第一次启动(非重建),该方法不会被调用。
注意:onCreate(Bundle) 也会收到相同的 Bundle,但 onRestoreInstanceState 在 onStart 之后调用,此时 View 层次已恢复,更适合恢复视图状态。两者只用其一即可,推荐在 onRestoreInstanceState 中处理恢复逻辑。
1.4 生命周期与重建流程总览(SVG 图)
1.5 状态保存与恢复的典型实现
以下代码演示如何保存编辑框中的文本、滚动位置等瞬时状态:
// 保存状态
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("edit_text", editText.getText().toString());
outState.putInt("scroll_y", scrollView.getScrollY());
}
// 恢复状态(方式一:在 onRestoreInstanceState 中)
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
editText.setText(savedInstanceState.getString("edit_text", ""));
scrollView.scrollTo(0, savedInstanceState.getInt("scroll_y", 0));
}
}
最佳实践:不要依赖 onRestoreInstanceState 的 savedInstanceState 非空判断来区分“是否是重建”——应直接判断 Bundle 是否包含期望的 key。对于配置变更场景,View.onSaveInstanceState() 会自动保存 EditText 文本等状态,只有当自定义数据需要额外保存时才必须重写该方法。
1.6 常见错误与调试建议
- 错误1:在
onCreate中使用if (savedInstanceState != null)恢复数据,但忘记调用super.onCreate(savedInstanceState)导致 View 状态丢失。 - 错误2:混淆
onPause与onSaveInstanceState:onPause适合保存持久化数据(如写数据库),onSaveInstanceState适合保存运行时 UI 状态。 - 调试方法:在 Logcat 中过滤
ActivityThread或自定义日志,打印生命周期方法调用堆栈。配置变更时可使用adb shell am start -a android.intent.action.MAIN -n com.example/.MainActivity --ez "force" true触发模拟重建。
1.7 性能与稳定性优化要点
在 onSaveInstanceState 中只保存轻量数据(字符串、整数、简单 Bundle),避免保存 Bitmap、List<Object> 等大对象,否则可能导致 TransactionTooLargeException(Binder 缓存上限 1MB)。对于大型 UI 状态,建议使用 ViewModel + SavedStateHandle 替代手动保存。配置变更频繁的场景(如频繁旋转)可考虑使用 android:configChanges 或 Fragment 嵌套视图,但需同时处理好生命周期解耦。