standardsingleTopsingleTasksingleInstance
它们主要解决的问题是:
当启动一个 Activity 时,系统是每次都创建新实例,还是复用已有实例?
配置方式一般是在 AndroidManifest.xml 中:
<activity
android:name=".MainActivity"
android:launchMode="standard" />
一、standard:标准模式
1. 特点
standard 是默认启动模式。
每次启动 Activity,都会创建一个新的实例,不管任务栈里是否已经存在这个 Activity。
例如:
启动顺序:
A -> B -> B -> B
任务栈中会变成:
栈顶
B
B
B
A
栈底
每次 startActivity() 都会执行新的:
onCreate()
2. 常见使用场景
适合大多数普通页面。
例如:
- 新闻详情页
- 商品详情页
- 聊天详情页
- 文章详情页
- 普通表单页面
比如你从不同文章列表点进不同文章详情:
文章列表 -> 文章详情1 -> 文章详情2 -> 文章详情3
这种情况下每个详情页可以是不同内容,使用 standard 很合理。
3. 示例
<activity
android:name=".DetailActivity"
android:launchMode="standard" />
4. 注意点
如果一个页面不希望重复创建,就不要使用 standard。
比如登录页、首页、启动页等,如果重复打开多个实例,可能会导致返回栈混乱。
二、singleTop:栈顶复用模式
1. 特点
如果要启动的 Activity 已经在当前任务栈的栈顶,则不会创建新实例,而是复用当前实例,并回调:
onNewIntent(Intent intent)
如果它不在栈顶,仍然会创建新的实例。
例如当前栈:
栈顶
B
A
栈底
此时再次启动 B:
startActivity(new Intent(this, B.class));
因为 B 已经在栈顶,所以不会创建新 B:
栈顶
B
A
栈底
执行:
B.onNewIntent()
但是如果当前栈是:
栈顶
C
B
A
栈底
此时再启动 B,因为 B 不在栈顶,所以会创建新的 B:
栈顶
B
C
B
A
栈底
2. 常见使用场景
适合那些:
页面可能会被连续重复打开,但如果它已经在最上面,就没必要再创建一个。
典型场景:
场景一:通知栏点击进入消息页
比如用户连续点击同一个通知:
通知 -> MessageActivity
通知 -> MessageActivity
通知 -> MessageActivity
如果用 standard,可能创建多个 MessageActivity。
如果用 singleTop,当 MessageActivity 已经在栈顶时,就复用它。
场景二:搜索页面
用户在搜索页中再次触发搜索跳转到搜索页,如果页面已经在顶部,就复用当前页面。
场景三:浏览器地址栏页面
当前已经是某个浏览页面,外部再次传入新的 URL,可以用 singleTop 复用栈顶页面,然后在 onNewIntent() 中处理新 URL。
3. 示例
<activity
android:name=".SearchActivity"
android:launchMode="singleTop" />
处理新的 Intent:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
String keyword = intent.getStringExtra("keyword");
// 根据新的 keyword 更新页面
}
4. 对应 Intent Flag
singleTop 类似于:
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
三、singleTask:栈内复用模式
1. 特点
singleTask 表示:
在同一个任务栈中,只允许存在一个该 Activity 实例。
当启动这个 Activity 时:
- 如果任务栈中不存在它:创建新实例
- 如果任务栈中已经存在它:复用已有实例,并把它上面的 Activity 全部清除,然后回调
onNewIntent()
例如当前任务栈:
栈顶
D
C
B
A
栈底
如果 B 是 singleTask,此时再次启动 B:
栈顶
B
A
栈底
C、D 会被清除,B 被复用,执行:
B.onNewIntent()
这叫做:
Clear Top + 复用
2. 常见使用场景
适合那些:
在整个任务栈中只应该存在一个实例的核心入口页面。
典型场景:
场景一:首页 MainActivity
很多 App 的首页会配置为 singleTask。
比如从通知、推送、Scheme、外部链接再次进入 App 时,希望回到已有首页,而不是重新创建多个首页。
MainActivity -> DetailActivity -> PayActivity
此时收到推送,点击进入 MainActivity,如果 MainActivity 是 singleTask:
MainActivity
上面的 DetailActivity、PayActivity 会被清掉。
适合“回到首页”的需求。
场景二:登录页 LoginActivity
有些项目会将登录页设计为唯一实例,避免出现多个登录页叠加。
不过登录页是否适合 singleTask 要看业务。
例如 token 失效时,无论当前在哪个页面,都跳到登录页,并清除它上面的页面。
场景三:支付结果入口页
支付结果页、订单确认页等,如果不希望反复创建多个实例,可以考虑 singleTask。
场景四:Deep Link / Scheme 入口页
比如外部浏览器打开:
myapp://product?id=100
如果入口 Activity 使用 singleTask,可以复用已有实例,然后在 onNewIntent() 中处理新的参数。
3. 示例
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />
处理新 Intent:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
String from = intent.getStringExtra("from");
// 处理新的跳转逻辑
}
4. 注意点
singleTask 会清除目标 Activity 上面的页面。
比如:
MainActivity -> ListActivity -> DetailActivity
如果 MainActivity 是 singleTask,再次启动 MainActivity:
MainActivity
ListActivity 和 DetailActivity 会被销毁。
所以它适合“回到某个根页面”的需求,但不适合普通详情页。
5. 对应 Intent Flag
类似于:
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
但不完全等价,因为 singleTask 还涉及 taskAffinity 和任务栈选择。
四、singleInstance:单实例独立栈模式
1. 特点
singleInstance 比 singleTask 更严格。
特点:
- 全局只允许存在一个该 Activity 实例
- 该 Activity 独占一个任务栈
- 其他 Activity 不会和它在同一个任务栈中
例如启动流程:
A -> B(singleInstance) -> C
任务栈可能是:
任务栈1:
A -> C
任务栈2:
B
B 独占一个任务栈。
2. 常见使用场景
现在实际开发中用得比较少。
适合那些:
需要全局唯一,并且要和其他页面完全隔离的 Activity。
典型场景:
场景一:来电页面
系统电话来电界面类似这种设计。
来电页面需要独立存在,不希望被普通 App 页面干扰。
场景二:闹钟响铃页面
闹钟响铃页可能需要从任何地方唤起,并且保持全局唯一。
场景三:悬浮/独立工具页面
某些独立功能页面,例如全局播放器控制页、独立视频通话页等,可能考虑 singleInstance。
场景四:地图导航页面
某些导航类页面需要全局唯一、独立任务栈,也可能使用。
不过一般情况下,singleTask 已经够用。
3. 示例
<activity
android:name=".CallActivity"
android:launchMode="singleInstance" />
4. 注意点
singleInstance 会带来任务栈切换问题。
比如:
A -> B(singleInstance) -> C
按返回键时,用户看到的返回路径可能和预期不一样,因为 B 在独立任务栈中。
所以普通业务页面不建议使用 singleInstance。
五、四种启动模式对比
| 启动模式 | 是否默认 | 是否复用 | 复用条件 | 是否清除上层页面 | 常见场景 |
|---|---|---|---|---|---|
standard |
是 | 否 | 每次都新建 | 否 | 普通页面、详情页 |
singleTop |
否 | 是 | 目标 Activity 在栈顶 | 否 | 通知页、搜索页、防止连续点击重复打开 |
singleTask |
否 | 是 | 栈中已有该 Activity | 是,清除其上方页面 | 首页、Scheme 入口、主入口页 |
singleInstance |
否 | 是 | 全局已有该 Activity | 独立任务栈 | 来电、闹钟、独立视频通话页 |
六、生命周期回调区别
假设目标 Activity 已经存在并被复用,则不会走:
onCreate()
而是走:
onNewIntent()
通常会执行类似流程:
onPause()
onNewIntent()
onResume()
所以如果你在 singleTop、singleTask、singleInstance 中需要处理新参数,要重写:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
// 处理新的 intent 数据
}
注意:
setIntent(intent);
如果你后续还会通过:
getIntent()
获取最新数据,建议调用 setIntent(intent)。
七、实际开发怎么选?
1. 普通页面
例如:
- 商品详情
- 新闻详情
- 设置页
- 列表页
一般用默认:
android:launchMode="standard"
2. 防止重复打开当前页面
例如:
- 当前已经在搜索页,再次打开搜索页
- 当前已经在消息页,再次点击通知进入消息页
可以用:
android:launchMode="singleTop"
3. App 首页、主入口
例如:
- MainActivity
- 首页 Tab 容器
- Scheme 分发入口
常用:
android:launchMode="singleTask"
4. 全局独立页面
例如:
- 来电页
- 闹钟页
- 独立通话页
可以考虑:
android:launchMode="singleInstance"
但普通 App 很少用。
八、简单记忆
可以这样记:
standard:
每次都创建新的。
singleTop:
如果在栈顶,就复用。
singleTask:
如果栈里有,就复用,并清掉它上面的页面。
singleInstance:
全局唯一,自己单独一个任务栈。
最常见使用:
普通详情页:standard
防重复点击:singleTop
首页/主入口:singleTask
独立特殊页面:singleInstance