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 图)

Activity 生命周期与重建触发流程 onCreate onStart onResume onPause onSaveInstanceState onStop onRestoreInstanceState onDestroy 重建 重建时跳回 onCreate 仅重建时 说明: • 正常流程:onCreate → onStart → onResume → onPause → onStop → onDestroy • 配置变更或进程被杀死时,onPause 之后会调用 onSaveInstanceState 保存状态。 • 重建后,onStart 之后调用 onRestoreInstanceState 恢复状态(如果 Bundle 非空)。 • 用户按 Back 键主动关闭 Activity 时,不会触发重建,onSaveInstanceState 也不会被调用。 • onRestoreInstanceState 在 onStart 之后、onResume 之前调用,此时 View 树已完全恢复。

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 嵌套视图,但需同时处理好生命周期解耦。