在 Android 10+ 中,如何处理后台 Activity 被系统回收后的状态恢复?

解读

国内 ROM 在内存紧张时比 AOSP 更激进,后台进程几分钟就可能被“杀缓存”。面试时,面试官想确认两点:

  1. 你是否真的遇到过“页面重启”导致的空指针 / Fragment 重叠 / 数据丢失;
  2. 你是否能把“系统回收 → 进程死亡 → 重新创建”整条链路讲清楚,并给出可落地的代码级方案,而不是背官方文档。
    回答要围绕“保存-恢复”闭环,兼顾 Java/Kotlin 双语言,并点出 Android 10 之后 SavedStateHandle、自动恢复限制等新变化。

知识点

  1. 回收触发条件:Low Memory Killer、厂商后台管控、adb shell am kill、用户“划掉”任务。
  2. 重建流程:系统通过 ActivityThread 重新创建 Application → 反射构造 Activity → 调用 attach() → 分发 onCreate(savedInstanceState)。
  3. 状态载体:
    • Activity/Fragment 的 onSaveInstanceState(Bundle) / SavedStateRegistry
    • View 的 android:saveEnabled + onSaveInstanceState / onRestoreInstanceState
    • Jetpack ViewModel 的 SavedStateHandle(Android 10 正式引入)
    • 非 UI 数据:Room、DataStore、MMKV、文件、云端同步
  4. 大小限制:Binder 1 MB 事务缓冲区,单个 Bundle 超过 ~200 k 会触发 TransactionTooLargeException;图片、列表等不能直写 Bundle。
  5. 恢复时机:onCreate / onRestoreInstanceState;Fragment 在 onViewStateRestored;Compose 用 rememberSaveable。
  6. 国内特殊场景:
    • 微信、QQ 保活白名单不可依赖;
    • 小米/华为“锁屏清理内存”会把整个进程杀掉,重启后无回调,只能靠持久化;
    • 部分 ROM 会屏蔽 START_ACTIVITIES_FROM_BACKGROUND,跳转前需加 FOREGROUND_SERVICE 权限或用户可见窗口。
  7. 调试命令:
    adb shell am kill <package>
    adb shell cmd activity stop-app <package>
    开发者选项“不保留活动”模拟极端回收。

答案

“我在线上通过 Bugly 捕获过大量 IllegalStateException: Can not perform this action after onSaveInstanceState,根本原因是进程被杀后重建时 Fragment 事务提交时机不对。针对 Android 10+,我分四层做状态恢复:

  1. UI 轻量状态:
    在 Activity 与 Fragment 中统一重写 onSaveInstanceState(outState: Bundle),只放用户当前阅读位置、选中 Tab 等基础字段;对 RecyclerView 的滚动位置使用 ParcelableUtil 封装,防止过大。
  2. 业务数据:
    把列表数据、搜索关键字放到 ViewModel 的 SavedStateHandle,通过 handle.getLiveData() 自动恢复;超过 Binder 限制的数据走 Room 缓存,key 存数据库主键,Bundle 只写主键。
  3. 大图与缓存:
    图片 URL 与 Glide 缓存 key 持久化到 DataStore,重建时按屏幕宽度重新采样,避免 TransactionTooLarge。
  4. 兜底策略:
    在 Application.onCreate 里读取最后一次 Activity 记录(SP 存 simpleName + intent uri),若检测到非正常死亡且用户 30 分钟内返回,弹半屏‘继续浏览’浮层,点击后带 FLAG_ACTIVITY_CLEAR_TOP 重启原页面并拉取最新数据,保证体验闭环。
    上线后同类崩溃从 0.8% 降到 0.02%,后台统计用户重进流失率下降 35%。”

拓展思考

  1. 如果业务是音视频播放,进度条状态放 SavedStateHandle 仍可能丢 3~5 秒,可结合 MediaSession + PlaybackState 持久化到 Room,并在 onPrepared 时 seekTo(lastPosition - 2s) 做补偿。
  2. 多模块组件化场景,各业务模块可自定义 SavedStateProvider 注册到 Activity 的 SavedStateRegistry,实现“谁产生状态谁负责恢复”,避免在基类 Activity 写一堆 if-else。
  3. Android 12 引入的“自动重置权限”会让后台被杀后首次启动缺失权限,需在恢复流程中先检查权限再渲染 UI,否则状态恢复成功但功能仍不可用。