使用 singleTask 模式时,如果目标栈中已存在该 Activity,会发生什么?如何控制其行为?
解读
国内面试官问“singleTask 已存在会怎样”,核心想确认两点:
- 对 AMS(ActivityManagerService)调度逻辑是否真正读过源码,而不是背“栈内复用”四个字;
- 能否把“复用”带来的副作用(清空顶部、onNewIntent 时机、生命周期顺序、返回栈变化)讲清楚,并给出可落地的控制方案。
答得太浅(只说“会复用”)会被追问“复用后按返回键为什么不是按原来顺序退出”,答得太偏(把 singleInstance 混进来)又显得边界感不足。必须围绕 TaskAffinity、Intent.FLAG、launchMode 三要素,把“系统行为”与“业务可控制点”拆开说。
知识点
- Task 与 ActivityRecord:AMS 以 TaskRecord 为单位管理 Activity,Task 由 TaskAffinity 字符串唯一标识;singleTask 的 Activity 必须有一个 Affinity,默认等于包名。
- 复用条件:新启动的 singleTask Activity 的 Affinity 与已存在的 Task 的 Affinity 相等,且该 Task 里已有同 java class 的实例。
- 系统行为:
a. 清空该 Task 内位于该 Activity 之上的所有 Activity(clear top);
b. 调用已存在实例的 onNewIntent(),生命周期顺序为:
原栈顶 Activity 走 onPause → 被清掉的 Activity 依次 onPause→onStop→onDestroy → 复用 Activity 走 onNewIntent→onRestart→onStart→onResume;
c. 若 Intent 携带 FLAG_ACTIVITY_CLEAR_TASK 或 FLAG_ACTIVITY_NEW_TASK 组合,会把整 Task 清空再重建,与 singleTask 语义叠加。 - 返回栈影响:复用后 Task 只剩 singleTask 实例及其以下 Activity,用户按返回键将按剩余栈顺序退出,不会回到被清掉的页面,因此支付、登录等场景必须评估是否可接受。
- 可控手段:
- 在 AndroidManifest 中显式声明 android:taskAffinity 把 singleTask Activity 放到独立 Task,避免误杀主 Task;
- 启动时加 FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP 可模拟 singleTask 但不清 Task,仅当栈顶已是目标时复用;
- 在 onNewIntent() 里通过 setIntent() 更新最新 Intent,并手动刷新 UI,防止展示旧数据;
- 若需“仅复用不清栈”,改用 singleTop 或手动管理 Fragment;
- 对车载、TV 等多窗口设备,需额外检查 Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT,防止 Task 被移到副屏导致生命周期错乱。
答案
当目标 Task 中已存在该 singleTask Activity 实例时,系统会:
- 把该 Task 内位于它之上的所有 Activity 全部出栈并销毁;
- 将新 Intent 通过 onNewIntent() 派发给已存在的实例,生命周期顺序为:
原栈顶 onPause → 被清 Activity 依次 onPause→onStop→onDestroy → 复用实例 onNewIntent→onRestart→onStart→onResume; - 复用后 Task 只剩该实例及其下方 Activity,返回键不再经过被清掉的页面。
控制行为的方法:
- 用 android:taskAffinity 把 singleTask Activity 隔离到独立 Task,避免误清空主流程;
- 启动时谨慎使用 FLAG_ACTIVITY_CLEAR_TASK / FLAG_ACTIVITY_NEW_TASK,防止叠加语义导致整 Task 被重建;
- 在 onNewIntent() 中调用 setIntent(intent) 并刷新数据,确保用户看到最新内容;
- 若业务不允许“清栈”,则改用 singleTop 或在 Intent 中去掉默认的 FLAG_ACTIVITY_CLEAR_TOP,通过 Fragment 栈自行管理页面。
拓展思考
- 国内厂商 ROM 对 Task 的“后台锁定”策略(如 MIUI 的“后台管理”)可能把 singleTask 所在 Task 整个杀掉,导致下次启动走全新 onCreate 而非 onNewIntent,需在后台保活与 Affinity 隔离之间权衡。
- 在折叠屏或大屏幕设备上,若应用声明了 android:resizeableActivity="false",singleTask 复用时会触发 AMS 的 bounds 重新计算,可能出现 onMultiWindowModeChanged 回调,需在 onNewIntent 里同步更新布局。
- 支付类 SDK 常要求“透明 Activity 用 singleTask”以保证快速拉起,但清栈会导致用户按返回键直接回到桌面,体验断裂;可额外加一层透明 singleTop Activity 做中转,把返回路径补全。