1. Activity重建的触发场景:系统配置变更、后台进程被杀死、资源内存不足
Activity 重建是 Android 生命周期中影响导航状态最直接的破坏性事件。理解触发场景是保存与恢复导航状态的第一步。根据触发原因,重建可分为三大类:系统配置变更、后台进程被杀死、资源内存不足。以下逐一剖析原理与行为差异。
1.1 场景对比总览
| 触发场景 | 典型原因 | 用户感知 | 保存/恢复时机 | 导航状态丢失风险 |
|---|---|---|---|---|
| 系统配置变更 | 屏幕旋转、语言切换、density 变化 | 瞬时闪屏(可见) | onSaveInstanceState / onCreate | 中(系统自动保存部分 View 状态) |
| 后台进程被杀死 | 系统低内存、用户设置“不保留活动” | 返回时重新启动 | onSaveInstanceState / onCreate + Bundle | 高(需手动保存 Fragment BackStack) |
| 资源内存不足 | 单个 App 内存占用超限、系统全局内存压力 | 闪退或 ANR 后恢复 | onSaveInstanceState 可能不保证触发 | 极高(需要持久化方案) |
1.2 系统配置变更
当设备配置(orientation、locale、screenSize 等)发生变化时,Android 默认行为会销毁当前 Activity 并立即重建,以匹配新配置下的资源。这是最常见的重建场景,用户可以直接观察到界面闪烁。
- 关键回调顺序:
onPause() → onSaveInstanceState() → onStop() → onDestroy() → onCreate() → onStart() → onRestoreInstanceState() - 系统自动保存:系统为每个 View 调用
onSaveInstanceState(),如果android:id存在且 View 可保存,系统会恢复如 EditText 内容、ScrollView 位置等。 - 导航状态痛点:Fragment 的 BackStack 不会被系统自动保存,需在
onSaveInstanceState中手动保存回退栈。
💡 避免重建:在 AndroidManifest.xml 中通过
android:configChanges="orientation|screenSize" 声明不触发重建,但仅推荐在简单界面中使用,否则会导致资源错配。
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|uiMode" />
// 重写 onConfigurationChanged 处理逻辑
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 手动刷新资源
recreate(); // 不推荐
}
1.3 后台进程被杀死
系统在内存紧张时按 LRU 列表 杀死后台进程。Activity 未被 finish 但进程已被 System Server 标记为可回收。用户从最近任务或通过 Intent 返回时,Activity 会重建。
- 触发条件:
ActivityManager.getProcessMemoryTrimLevel()达到TRIM_MEMORY_RUNNING_MODERATE及以上;开发者选项中开启“不保留活动”强制模拟。 - 与旋转重建的区别:进程被杀时
onSaveInstanceState仍然会被调用(系统会保留一个“被杀前快照”Bundle),但Application对象、静态变量全部丢失。 - 导航状态保存策略:必须依赖
Bundle或ViewModel+SavedStateHandle。
// 在 Fragment 中手动保存 BackStack
public class NavigationFragment extends Fragment {
private NavController navController;
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存当前 NavBackStackEntry 的 ID
NavBackStackEntry currentEntry = navController.getCurrentBackStackEntry();
if (currentEntry != null) {
outState.putString("current_destination", currentEntry.getDestination().getId());
}
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
String savedDestId = savedInstanceState.getString("current_destination");
if (savedDestId != null) {
navController.navigate(NavDestination.create(savedDestId));
}
}
}
}
1.4 资源内存不足
当 App 申请的堆内存超过 Dalvik/ART 限制,或全局内存压力巨大时,系统可能直接 kill 进程而不调用 onSaveInstanceState(尤其当应用处于 Background 状态且系统判断恢复成本过高时)。
⚠️ 关键风险:此场景下
onSaveInstanceState 不保证会被执行。导航状态丢失概率最高,必须结合持久化存储(如 Room、DataStore)或云端备份。
- 检测内存压力:通过
Application.onTrimMemory()的TRIM_MEMORY_COMPLETE判断。 - 推荐方案:在关键导航节点(如完成表单步骤)主动将导航栈快照写入 SharedPreferences 或本地数据库。
// 持久化存储导航栈示例(使用 Gson + SharedPreferences)
public class NavigationStatePersistence {
private static final String KEY_NAV_STACK = "nav_back_stack";
public static void saveNavStack(Context context, NavController navController) {
List<String> destinationIds = new ArrayList<>();
NavBackStackEntryIterator iterator = navController.iterator();
while (iterator.hasNext()) {
destinationIds.add(iterator.next().getDestination().getId());
}
String json = new Gson().toJson(destinationIds);
context.getSharedPreferences("nav", Context.MODE_PRIVATE)
.edit()
.putString(KEY_NAV_STACK, json)
.apply();
}
public static List<String> loadNavStack(Context context) {
String json = context.getSharedPreferences("nav", Context.MODE_PRIVATE)
.getString(KEY_NAV_STACK, null);
if (json == null) return new ArrayList<>();
return new Gson().fromJson(json, new TypeToken<ArrayList<String>>(){}.getType());
}
}
1.5 三种场景的触发流程示意
1.6 如何区分当前重建类型
在 onCreate 中通过 savedInstanceState 与 intent.getFlags() 进行判断,有助于采取不同恢复策略。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
// 配置变更或进程被杀死
boolean isColdStart = (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0;
if (isColdStart) {
// 进程被杀死,需要恢复导航栈
restoreNavigationFromPersistence();
} else {
// 配置变更,直接使用 bundle 恢复
restoreNavigationFromBundle(savedInstanceState);
}
} else {
// 全新启动或内存不足没保存
setupFreshNavigation();
}
}