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 三种场景的触发流程示意

Activity 重建三大触发场景流程 Activity 运行中 系统配置变更 后台进程被杀死 资源内存不足 Activity 重建 系统自动保存 View 状态 需手动保存导航栈 说明:系统配置变更与后台进程被杀死均会触发 onSaveInstanceState;资源内存不足时该方法可能不会执行

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();
    }
}