请解释 Android 中四种启动模式(standard, singleTop, singleTask, singleInstance)的区别和适用场景

解读

国内大厂面试时,这道题几乎必问,但面试官真正想听的不是“官方定义”,而是“你在真实业务里怎么选、怎么踩坑、怎么兜底”。
因此回答思路要“先结论、后原理、再案例、再坑点”,用“一句话区别 + 一句话原理 + 一句话场景 + 一句话坑”四段式展开,既体现深度又控制时长。
注意:国内项目普遍接多渠道 SDK、深度定制 ROM,要把“任务栈被厂商魔改”这一现实考虑进去,否则会被追问“为什么 singleTask 在小米上失效”。

知识点

  1. 任务栈(Task)与 ActivityRecord:AMS 管理的历史栈单元,每个 Task 对应一个 Stack,启动模式决定新 Activity 实例与 Task 的归属关系。
  2. Intent.FLAG_ACTIVITY_* 与 launchMode 的叠加规则:代码动态 flag 优先级高于 manifest 静态声明,国内很多推送 SDK 偷偷加 FLAG_NEW_TASK,会把你的 singleTop 冲掉。
  3. clearTop 与 reorder 行为:singleTask 自带 CLEAR_TOP 语义,但国产 ROM 可能把 TaskAffinity 重置,导致回不到旧栈。
  4. 复用生命周期回调:singleTop 的 onNewIntent、singleTask 的 onNewIntent + onActivityResult 失效场景(Android 10 之后 startActivityForResult 被废弃,需用 Activity Result API)。
  5. 折叠屏/多窗口下的栈可见性:singleInstance 在副屏打开时,某些 ROM 会强制把实例挪回主屏,导致透明主题闪屏。
  6. 国内厂商“保活”陷阱:singleInstance 被系统单独放一栈,容易被省电策略杀掉,推送到达率反而下降。

答案

  1. standard
    一句话区别:每次启动必创全新实例,无论栈内是否已有。
    原理:AMS 在调用者所在 Task 顶部直接新建 ActivityRecord,生命周期完整走 onCreate→onStart→onResume。
    场景:普通二级页面,如商品详情、文章页,允许无限层级回退。
    坑:快速双击时瞬间压两实例,需在 onCreate 做防抖或 launchMode 临时改 singleTop。

  2. singleTop
    一句话区别:栈顶复用,非栈顶仍创实例。
    原理:AMS 发现目标 Task 栈顶已存在该 ActivityRecord,则走 onNewIntent,不弹新实例;否则同 standard。
    场景:搜索页、扫码页,防止推送/通知反复打开多层。
    坑:国内推送 SDK 默认加 FLAG_ACTIVITY_NEW_TASK,会把当前页挪到新 Task,singleTop 失效;需在 receiver 里清空 flag 或强制 Intent.FLAG_ACTIVITY_CLEAR_TOP。

  3. singleTask
    一句话区别:全局单例,所在 Task 只允许一个实例,启动时自动清掉其上方所有 Activity。
    原理:AMS 先按 TaskAffinity 找对应 Task,找到后把目标实例提到栈顶并弹掉上方所有 Activity,触发 onNewIntent;若 Task 不存在则新建 Task 再新建实例。
    场景:应用首页、WebView 容器、播放器,保证回首页时一次性清空中转页。
    坑:小米/华为 ROM 对 TaskAffinity 做“魔改合并”,可能出现首页被拉到后台 Task,导致 singleTask 重建;兜底方案:首页 android:taskAffinity="" 强制默认包名,配合 Activity Result API 处理回调。

  4. singleInstance
    一句话区别:系统级单例,独占新 Task 且该 Task 永不放入其他 Activity。
    原理:AMS 新建独立 Task,taskType 为 SINGLE_INSTANCE,最近任务列表里单独卡片;任何其他 Activity 启动都会另开 Task。
    场景:系统来电、悬浮窗、全局投屏控制面板,需要与主应用完全隔离。
    坑:国内 ROM 省电策略优先杀“孤立 Task”,singleInstance 实例易被回收;且透明主题在折叠屏切换时闪黑;建议只在真正需要跨进程可见的组件使用,并加前台 Service 保活。

一句话总结选型口诀:
“普通页 standard,顶部复用 singleTop,首页根 singleTask,系统框 singleInstance。”

拓展思考

  1. Android 12 的“多实例会话”特性允许同一 Activity 以不同 session 多次出现在最近任务,launchMode 与 android:documentLaunchMode、android:maxRecents 如何协同?
  2. 在 Jetpack Navigation 单 Activity 架构下,launchMode 实际由 NavHostFragment 托管,Fragment 的“栈顶复用”能否用自定义 Navigator 实现 singleTop 效果?
  3. 国内小程序容器(微信、支付宝)把 Activity 做成 singleInstance,再用 IActivityManager.addAppTask 伪造最近任务卡片,如何规避 10 秒后台启动限制?
  4. 车载 Android(AAOS)对 singleInstance 做了“悬浮 Task”级别提升,允许在副驾屏常驻,如何兼容手机 APK 直接上车机而不改 launchMode?
  5. 隐私沙盒(Privacy Sandbox)限制 cross-task 启动,singleTask 的 TaskAffinity 匹配规则未来可能收紧,如何提前用 PendingIntent 封装跳转以降低适配成本?

答到这一层,面试官一般会收手,并给你打“熟悉系统源码、有国内落地经验”的标签。