从原理到避坑:全网最清晰的 Android Activity 启动模式全景指南
在 Android四大组件之 Activity 里我们说过:系统用一个栈来管理 Activity,“打开新页面就往栈顶放盘子,按返回就拿走栈顶盘子”。这是最简单的模型。但真实应用里有很多需求打破了这个简单模型:点通知不想重复打开同一个页面、从分享进来希望复用已有页面、主页希望始终只有一个……这些都靠启动模式(launchModel) 和**任务栈(Task)**来控制。这是 Activity 知识里最绕的一块,本文把它讲透。
一、先搞懂两个概念:Task 与 Back Stack
1.1 Task(任务)
Task 是用户完成某件事时所接触的一组 Activity 的集合。 它不等于应用——一个 Task 里的 Activity 可以来自不同应用。
举例:你在微信里点了一个链接,跳到浏览器看网页,再从网页分享回某个应用。这一连串 Activity(微信页 → 浏览器页 → 分享页)可能都待在同一个 Task 里,按返回键会依次退回。
1.2 Back Stack(返回栈)
每个 Task 内部用一个栈来组织 Activity,这就是返回栈。它遵循后进先出(LIFO):

- 启动新 Activity → 压入栈顶(push),它显示出来;
- 按返回键 → 弹出栈顶(pop)并销毁,下面那个重新显示;
- 栈空了 → Task 结束,回到桌面。
关键认知: 默认情况下,每次
startActivity()都会创建一个新实例压栈——哪怕是同一个 Activity,连点三次就有三个实例叠在一起。启动模式要解决的,正是"何时复用、何时新建"。
二、四种启动模式(launchMode)
启动模式在 AndroidManifest.xml 中通过 android:launchMode 声明:
<activity android:name=".MainActivity" android:launchMode="singleTask" />
2.1 standard(标准模式,默认)
每次启动都创建一个新实例,压入当前 Task 栈顶。 可以有任意多个实例,可以分布在不同 Task。
启动 A、A、A: [A1, A2, A3] ← 三个独立实例
- 场景: 绝大多数普通页面。
- 特点: 最简单,符合"一个动作一个页面"的直觉。
2.2 singleTop(栈顶复用)
如果目标 Activity 已经在栈顶,就复用它,不创建新实例;否则和 standard 一样新建。复用时会回调 onNewIntent()。
当前 [A, B],再启动 B: [A, B] ← B 在栈顶,复用,触发 onNewIntent
当前 [A, B],再启动 A: [A, B, A] ← A 不在栈顶,新建
- 场景: 通知点击最典型。连续收到多条通知点进同一个页面,不希望叠出一堆重复页面;搜索结果页反复搜索也常用。
- 要点: 复用时数据通过
onNewIntent(intent)传入,记得在这里更新界面,而不是只看onCreate里的旧数据。
2.3 singleTask(栈内复用,单实例于 Task)
整个 Task 里只允许存在一个该 Activity 的实例。 启动时:
- 若实例已存在,把它之上的所有 Activity 全部清除(弹出销毁),让它回到栈顶,并回调
onNewIntent(); - 若不存在,则新建。
当前 [A, B, C, D],启动 B(singleTask):
→ 清除 C、D,变成 [A, B],B 触发 onNewIntent
- 场景: 应用的主界面 / 首页。无论从哪里回到主页,都希望它是唯一的,并清掉它上面堆积的页面。
- 要点: 它有"清栈到自己"的副作用(clear top),上面的页面会被销毁。
2.4 singleInstance(全局单实例,独占一个 Task)
该 Activity 独占一个全新的 Task,且这个 Task 里永远只有它一个。 任何从它启动的其他 Activity 都会被放到别的 Task。
- 场景: 来电界面、系统级悬浮入口等需要"全局唯一、独立存在"的页面。日常业务极少用到。
- 要点: 它的 Task 里只有它自己,行为特殊,不要轻易使用。
四种模式对比
| 模式 | 是否复用 | 复用条件 | 是否触发 onNewIntent | 典型场景 |
|---|---|---|---|---|
standard |
否 | —— | 否 | 普通页面(默认) |
singleTop |
是 | 已在栈顶 | 是 | 通知、搜索结果页 |
singleTask |
是 | Task 内已存在(会清栈) | 是 | 主界面/首页 |
singleInstance |
是 | 独占 Task | 是 | 来电、全局唯一页 |
三、用 Intent Flag 动态控制启动行为
launchMode 写死在 Manifest 里。但很多时候我们想在代码里临时改变启动行为,这就用 Intent Flag。当两者冲突时,Flag 的优先级更高。
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
startActivity(intent)
常用 Flag
| Flag | 作用 | 对应/类似 |
|---|---|---|
FLAG_ACTIVITY_NEW_TASK |
在新 Task 中启动(从非 Activity 上下文如 Service 启动 Activity 时必须加) | —— |
FLAG_ACTIVITY_SINGLE_TOP |
栈顶复用 | ≈ singleTop |
FLAG_ACTIVITY_CLEAR_TOP |
若目标已在栈中,清除它之上的所有 Activity | 配合实现 singleTask 效果 |
FLAG_ACTIVITY_CLEAR_TASK |
启动前清空整个 Task(常与 NEW_TASK 连用) | 退出登录回到登录页常用 |
FLAG_ACTIVITY_NO_HISTORY |
该 Activity 离开后不留在栈中 | 一次性页面 |
退出登录的经典写法:
NEW_TASK | CLEAR_TASK启动登录页,清空所有历史,用户按返回也回不到登录前的页面。
四、影响任务栈的其他属性
除了 launchMode,Manifest 里还有几个属性会影响 Task 行为,了解即可:
| 属性 | 作用 |
|---|---|
taskAffinity |
指定 Activity "亲和"哪个 Task(默认是应用包名)。配合 singleTask / NEW_TASK 时决定它进入哪个 Task |
allowTaskReparenting |
允许 Activity 在条件满足时"改换门庭"到它更亲和的 Task |
clearTaskOnLaunch |
每次从桌面重新启动应用时,清空根 Activity 之上的所有页面 |
alwaysRetainTaskState |
让系统不要自动清理该 Task 的状态 |
建议:
taskAffinity等属性容易把任务栈搞乱、引发难以排查的导航 Bug,没有明确需求不要碰。绝大多数应用只用 launchMode + 少数几个 Flag 就够了。
五、onNewIntent:复用实例时别忘了它
只要发生了"复用已有实例"(singleTop / singleTask 命中、或 SINGLE_TOP / CLEAR_TOP 命中),系统不会再走 onCreate(),而是回调:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// 复用时新数据从这里来,必须在此更新界面
setIntent(intent) // 更新 Activity 持有的 intent
val id = intent.getIntExtra("id", -1)
refreshUi(id)
}
最常见的 Bug: 用 singleTop/singleTask 的页面,复用时界面没更新——因为开发者只在
onCreate里读了 Intent。记住:复用走的是onNewIntent,要在这里重新取数据并刷新。
六、现代视角:单 Activity 架构下还需要启动模式吗?
如前面 Fragment 篇 所讲,现代应用流行单 Activity 架构——一个 Activity 壳 + 多个 Fragment/Compose 目的地,页面跳转交给 Navigation 组件管理。
在这种架构下:
- 应用内的"页面跳转"大多发生在 Fragment / Compose 之间,用不到 launchMode;
- 但只要还涉及多 Activity、通知跳转、跨应用回流、第三方 SDK 入口,启动模式与任务栈依然是绕不开的基础;
- Navigation 也提供了对应概念(如
launchSingleTop、popUpTo)来表达类似"栈顶复用""清栈"的语义。
一句话: 即便你写的是单 Activity 应用,理解 Task 和启动模式仍然必要——因为系统级的导航、通知、跨应用跳转始终运行在这套机制之上。
参考来源:
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)