下面把 Activity 五种启动模式按"适用场景 → 典型搭配 → 返回栈行为 → 代码示例 → 易错点"讲清楚;最后给一张选型对照表与决策建议。
(五种=standard、singleTop、singleTask、singleInstance、singleInstancePerTask〔Android 12+〕)
1)standard(默认,能多开)
适用场景
-
常规页面:详情页、设置子页、搜索结果页等。
-
同一路由可允许多个实例并存(如 A→详情(1)→详情(2)...)。
常配 flags
-
默认无;需要切栈/新任务时才加 FLAG_ACTIVITY_NEW_TASK(少见于单进程 App)。
-
返回栈按"谁先开谁后退"。
示例
ini
<activity android:name=".ui.DetailActivity" android:launchMode="standard"/>
kotlin
startActivity(Intent(this, DetailActivity::class.java)
.putExtra("id", 123L))
易错点
- 误以为返回时会"合并同类项",不会;每次 startActivity 都是新实例。
2)singleTop(顶复用,不顶就新建)
适用场景
-
通知点进的"唯一入口页"(消息中心、会话页)------若已在栈顶,直接复用,避免"连点生成一串重复页面"。
-
顶部 Tab 容器/Home:从 Deep Link/通知再次进入时不重建,而是 onNewIntent() 刷新内容。
常配 flags
- FLAG_ACTIVITY_SINGLE_TOP(等价 Manifest 写 singleTop)。
- 常与 FLAG_ACTIVITY_CLEAR_TOP同用:若目标页已在栈内但不在顶,则清掉它上面的页面再复用它。
less
startActivity(Intent(this, HomeActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra("tab", "inbox"))
返回栈
- 若目标已是栈顶→不建新实例,走 onNewIntent();否则新建并入栈。
易错点
- singleTop 只在"目标位于栈顶"时复用;不在顶仍会新建。想"清到它并复用",记得加 CLEAR_TOP。
3)singleTask(任务级唯一,清到它 + 复用)
适用场景
-
全局任务入口/外壳页:如 MainActivity、路由中转页、认证后回到唯一主页的场景。
-
从外部唤起(Deep Link / 分享回流 / 第三方跳转)但希望"回到已有任务的入口并清理中间页"。
常配 flags / 属性
-
Manifest 直接写 singleTask;必要时配 taskAffinity 将其固定在某个 task。
-
代码侧常见 Intent.FLAG_ACTIVITY_NEW_TASK(跨进程/服务启动时),系统会把已有实例的 task 前置 并 onNewIntent(),同时把它上面的页面清掉。
返回栈
-
已存在实例→把该 task 前置,清栈到它,并回调 onNewIntent()。
-
不存在实例→新建一个以它为根的 task。
示例
ini
<activity
android:name=".ui.MainActivity"
android:launchMode="singleTask"
android:exported="true"/>
易错点
- 二次进入会"清到它",上面的页面会被移除;不适合用于需要"多层历史可回退"的页面。
4)singleInstance(独占一栈,全局仅一实例)
适用场景(非常少用)
-
来电界面、锁屏器、系统级浮层场景:这一个 Activity 必须独占一个任务栈,栈里不能再有别的页面。
-
从它再 startActivity(),新页面会被放到另一个 task。普通 App 几乎不需要。
返回栈
- 只有它自己;切走/返回会在不同任务间切换。
示例
ini
<activity android:name=".ui.InCallActivity"
android:launchMode="singleInstance"
android:showOnLockScreen="true"
android:turnScreenOn="true"/>
易错点
- 用户体验割裂、跨任务切换多;除非强需求,不建议业务页使用。
5)singleInstancePerTask(Android 12+,每个任务栈最多一个该 Activity)
适用场景
-
多窗口/多文档 :同一路由可在不同 task 各有一个实例(比如"文档A"和"文档B"分别在两个任务里),但单个 task 内不会重复。
-
适合需要"每个任务的根页面唯一"的多任务产品设计。
返回栈
- 在各自的 task里做根;再次打开相同文档/会话时复用对应 task 的该实例并 onNewIntent();不同文档可开到新 task。
示例
ini
<activity android:name=".ui.EditorActivity"
android:launchMode="singleInstancePerTask"
android:exported="true"/>
易错点
- 与旧的 singleInstance 不同:不是"全局唯一",而是"每个任务栈唯一"。
典型业务场景配方
A. 通知点击 → 回到唯一主页并刷新
-
MainActivity:singleTop 或 singleTask
-
Intent:CLEAR_TOP | SINGLE_TOP(或直接 singleTask)
-
逻辑:在 onNewIntent() 解析 extras,切换 Tab/滚到锚点。
B. Deep Link(https App Links)→ 若已在前台则复用主页
-
Manifest:主页 singleTop(常见)/singleTask(需要清栈到主页时)。
-
链接命中后系统把 Intent 投递到 onNewIntent(),你解析路径路由到目标子页。
C. 登录完成后"回到首页并清理登录链路"
-
登录成功时 startActivity(MainActivity, flags=CLEAR_TOP|SINGLE_TOP),或者把 Main 设为 singleTask 并 startActivity(Main);
-
效果:清除登录过程页面,避免返回键又回到登录页。
D. 同一路由允许打开多个详情(从列表连续点击不同 item)
- 详情 Activity:用 standard;每次点击即新建,返回键逐个退出。
E. 多文档/多会话分别驻留不同任务(Android 12+)
- 编辑器/会话页:singleInstancePerTask;配合 FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK 按需创建新任务。
代码模板(组合常用)
singleTop + 清到它:
kotlin
fun Context.toHome(tab: String? = null) {
startActivity(Intent(this, HomeActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
putExtra("tab", tab)
})
}
singleTask 主页(外部唤起/清栈回家):
ini
<activity android:name=".ui.MainActivity"
android:launchMode="singleTask"
android:exported="true"/>
kotlin
startActivity(Intent(this, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) // 服务/广播里启动时常用
多文档(12+):
ini
<activity android:name=".ui.EditorActivity"
android:launchMode="singleInstancePerTask"/>
kotlin
startActivity(Intent(this, EditorActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = uri // 每个文档一个 task
addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
})
常见坑与验证要点
- onNewIntent() 不是生命周期重建:UI 需自己刷新(比如 Compose 用 mutableStateOf 承接新的参数)。
- CLEAR_TOP 行为:清除目标之上的页面;若目标为 singleTop,复用并触发 onNewIntent()。
- taskAffinity 不会单独造新任务 :是否新建取决于 launchMode/flags,taskAffinity 只决定"更偏向放进哪个 task"。
- 返回键逻辑 :变更 launchMode/flags 会改变 back stack,务必写出"期望的返回路径"用例并实机点按。
- 通知/Deep Link:优先让"容器/主页"具备 singleTop,避免重复页面;在 onNewIntent() 做路由。
选型对照(速览)
| 模式 | 复用条件 | 是否可多实例 | 常见用途 | 典型 flags |
|---|---|---|---|---|
| standard | 不复用 | 是(同 task 多个) | 普通详情/子页 | (无) |
| singleTop | 目标已在栈顶 | 视情况 | 通知入口、首页容器 | `CLEAR_TOP |
| singleTask | 任务内唯一,清到它 | 任务级唯一 | 全局入口/外壳、回主页 | NEW_TASK(跨进程) |
| singleInstance | 全局唯一,独占一栈 | 否 | 来电/锁屏等系统场景 | (谨慎使用) |
| singleInstancePerTask | 每个 task 一个 | 跨 task 可多个 | 多文档/多窗口 | `NEW_DOCUMENT |
实战建议(结论)
- 90% 的页面 → standard。
- 主页/容器 → singleTop(+必要时 CLEAR_TOP),需要"回家清栈"则考虑 singleTask。
- 系统级特殊页面才用 singleInstance。
- 需要多文档/多任务体验(12+)→ singleInstancePerTask 配 NEW_DOCUMENT。