Appearance
Android 启动模式与任务栈 - 全面详解
Activity 的启动模式和任务栈机制是 Android 面试中的高频考点,深入理解对于构建良好的应用体验至关重要。
目录
- 启动模式概述
- Standard 模式(默认)
- SingleTop 模式(栈顶复用)
- SingleTask 模式(栈内复用)
- SingleInstance 模式(独立栈)
- 任务栈管理
- Intent Flag 组合使用
- onNewIntent 处理
- ASCII 流程图
- 面试考点
1. 启动模式概述
1.1 四种启动模式对比
| 模式 | 说明 | 栈内复用 | 跨栈复用 | 典型场景 |
|---|---|---|---|---|
| Standard | 默认,每次创建新实例 | 否 | 否 | 普通页面 |
| SingleTop | 栈顶复用 | 是(仅栈顶) | 否 | 首页、列表页 |
| SingleTask | 栈内复用 | 是 | 是 | 首页、主页 |
| SingleInstance | 独立栈 | 是 | 是 | 电话、闹钟 |
1.2 设置方式
kotlin
// 方式 1:在 Manifest 中设置(推荐)
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />
// 方式 2:在代码中动态设置
val intent = Intent(this, TargetActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
// 方式 3:TaskAffinity 设置任务亲和性
<activity
android:name=".MainActivity"
android:taskAffinity="com.example.task.main" />1.3 启动模式优先级
┌─────────────────────────────────────────┐
│ 启动模式生效优先级(从高到低) │
├─────────────────────────────────────────┤
│ 1. Intent Flag(代码动态设置) │
│ 2. Manifest 声明 │
│ 3. Application 默认 │
│ 4. System 默认(Standard) │
└─────────────────────────────────────────┘
示例:
Manifest: SingleTask
Intent: FLAG_ACTIVITY_NEW_TASK
结果: 按 NEW_TASK 在新栈启动2. Standard 模式(默认)
2.1 特性说明
Standard 模式的特点:
- ✅ 每次启动都会创建新的实例
- ✅ 可以存在于多个任务栈中
- ✅ 默认启动模式
- ❌ 不复用已有实例
2.2 使用示例
xml
<activity
android:name=".DetailActivity"
android:launchMode="standard" />
<!-- 或者直接不写,默认就是 standard -->
<activity android:name=".DetailActivity" />2.3 执行流程
用户点击 → 创建新实例 → 压入当前栈 → 显示
┌─────────────────┐
│ Task A │
│ ┌─────────────┐│
│ │ MainActivity ││
│ ├─────────────┤│
│ │ DetailA ││ ← 第一次启动
│ ├─────────────┤│
│ │ DetailA ││ ← 第二次启动(新实例)
│ └─────────────┘│
└─────────────────┘2.4 典型场景
kotlin
// 场景 1:商品详情(每次进入都是新的)
class ProductDetailActivity : AppCompatActivity() {
// 用户从不同列表进入同一商品,每次都是新实例
// 可以有不同的返回路径
}
// 场景 2:网页浏览(历史记录)
class WebViewActivity : AppCompatActivity() {
// 每次打开新页面都是新实例
// 可以后退到上一个页面
}3. SingleTop 模式(栈顶复用)
3.1 特性说明
SingleTop 模式的特点:
- ✅ 如果实例在栈顶,则复用
- ✅ 如果实例不在栈顶,创建新实例
- ✅ 复用时会调用 onNewIntent()
- ❌ 仅栈顶时复用
3.2 使用示例
xml
<activity
android:name=".MainActivity"
android:launchMode="singleTop" />3.3 执行流程
场景 1:栈顶时复用
┌─────────────────┐
│ Task A │
│ ┌─────────────┐│
│ │ OtherActivity││
│ ├─────────────┤│
│ │ MainActivity ││ ← 栈顶,复用
│ └─────────────┘│
└─────────────────┘
用户再次启动 MainActivity
↓
复用了栈顶的 MainActivity
调用 onNewIntent()场景 2:不在栈顶时创建新实例
┌─────────────────┐
│ Task A │
│ ┌─────────────┐│
│ │ DetailActivity││
│ ├─────────────┤│
│ │ MainActivity ││ ← 不在栈顶
│ ├─────────────┤│
│ │ MainActivity ││ ← 创建新实例
│ └─────────────┘│
└─────────────────┘
用户再次启动 MainActivity
↓
创建新的 MainActivity 实例3.4 代码示例
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MainActivity", "onCreate")
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d("MainActivity", "onNewIntent: ${intent.getStringExtra("data")}")
// 更新 UI
updateUI(intent)
// 设置为当前 Intent
setIntent(intent)
}
private fun updateUI(intent: Intent) {
// 根据新 Intent 更新界面
}
}3.5 典型场景
kotlin
// 场景 1:首页(从通知栏点击返回)
class MainActivity : AppCompatActivity() {
// 从通知栏点击进入,如果已在栈顶,直接复用
// 避免创建多余的首页实例
}
// 场景 2:列表页(避免重复创建)
class SearchListActivity : AppCompatActivity() {
// 搜索后返回列表页,如果已在栈顶,复用并刷新数据
}
// 场景 3:搜索页面
class SearchActivity : AppCompatActivity() {
// 搜索功能,多次打开搜索时复用栈顶实例
}4. SingleTask 模式(栈内复用)
4.1 特性说明
SingleTask 模式的特点:
- ✅ 同一任务栈中只存在一个实例
- ✅ 跨任务栈也可以复用
- ✅ 复用时会清除上方所有 Activity
- ✅ 复用时会调用 onNewIntent()
4.2 使用示例
xml
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />4.3 执行流程
场景 1:栈内已存在实例
初始状态:
┌─────────────────┐
│ Task A │
│ ┌─────────────┐│
│ │ DetailActivity││
│ ├─────────────┤│
│ │ SearchActivity││
│ ├─────────────┤│
│ │ MainActivity ││ ← 已存在
│ └─────────────┘│
└─────────────────┘
用户再次启动 MainActivity
↓
清除上方的 Activity
复用 MainActivity
调用 onNewIntent()
最终状态:
┌─────────────────┐
│ Task A │
│ ┌─────────────┐│
│ │ MainActivity ││ ← 复用
│ └─────────────┘│
└─────────────────┘场景 2:跨栈复用
初始状态:
┌───────────────┐ ┌───────────────┐
│ Task A │ │ Task B │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ Activity1 │ │ │ │ MainActivity││ ← 已存在
│ └───────────┘ │ │ └───────────┘ │
└───────────────┘ └───────────────┘
从 Task A 启动 MainActivity
↓
切换到 Task B
清除 Task B 中 MainActivity 上方的 Activity
复用 MainActivity
调用 onNewIntent()4.4 代码示例
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MainActivity", "onCreate")
// 首次创建时的初始化
initFirstLaunch()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d("MainActivity", "onNewIntent")
// 复用时的处理
handleNewIntent(intent)
setIntent(intent)
}
private fun handleNewIntent(intent: Intent) {
val data = intent.getStringExtra("data")
// 更新 UI 或执行操作
}
}4.5 典型场景
kotlin
// 场景 1:应用首页(最常用)
class MainActivity : AppCompatActivity() {
// 保证首页只有一个实例
// 从任何地方返回主页都清除中间页面
}
// 场景 2:浏览器主页
class BrowserHomeActivity : AppCompatActivity() {
// 浏览器的主页,始终只有一个实例
}
// 场景 3:登录页面
class LoginActivity : AppCompatActivity() {
// 登录后清除登录页,防止重复登录
}4.6 SingleTask + FLAG_ACTIVITY_CLEAR_TOP
kotlin
// 配合使用,确保复用并清除上方所有 Activity
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)5. SingleInstance 模式(独立栈)
5.1 特性说明
SingleInstance 模式的特点:
- ✅ 独占一个任务栈
- ✅ 栈中只能有这一个 Activity
- ✅ 其他 Activity 会在新的任务栈中打开
- ✅ 系统级别的复用
5.2 使用示例
xml
<activity
android:name=".PhoneActivity"
android:launchMode="singleInstance" />5.3 执行流程
初始状态:
┌───────────────┐
│ Task A │
│ ┌───────────┐ │
│ │ MainActivity││
│ └───────────┘ │
└───────────────┘
启动 SingleInstance 的 PhoneActivity
↓
创建新的 Task B,PhoneActivity 独占
┌───────────────┐ ┌───────────────┐
│ Task A │ │ Task B │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ MainActivity││ │ │ PhoneActivity││ ← 独占
│ └───────────┘ │ │ └───────────┘ │
└───────────────┘ └───────────────┘
从 PhoneActivity 打开 DetailActivity
↓
创建新的 Task C
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Task A │ │ Task B │ │ Task C │
│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
│ │ MainActivity││ │ │ PhoneActivity││ │ │ DetailActivity││
│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
└───────────────┘ └───────────────┘ └───────────────┘5.4 代码示例
kotlin
class PhoneActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("PhoneActivity", "onCreate - 独占栈")
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d("PhoneActivity", "onNewIntent - 复用")
// 处理新的来电信息
handleNewCall(intent)
}
private fun handleNewCall(intent: Intent) {
val phoneNumber = intent.getStringExtra("phone_number")
// 显示来电信息
}
}5.5 典型场景
kotlin
// 场景 1:电话应用
class PhoneActivity : AppCompatActivity() {
// 电话界面独占栈
// 不被其他应用干扰
}
// 场景 2:闹钟提醒
class AlarmActivity : AppCompatActivity() {
// 闹钟响起时独占栈
// 确保用户能及时处理
}
// 场景 3:短信回复
class SmsReplyActivity : AppCompatActivity() {
// 短信回复界面独占
}6. 任务栈管理
6.1 什么是任务栈
任务栈(Task Stack)是一个 LIFO(后进先出)的 Activity 集合,用于管理 Activity 的导航流程。
┌───────────────────────────┐
│ 任务栈 (Task) │
├───────────────────────────┤
│ Activity N (栈顶) │ ← 用户按 Back 返回上一个
│ Activity N-1 │
│ Activity N-2 │
│ ... │
│ Activity 2 │
│ Activity 1 (栈底/根) │ ← 通常是首页
└───────────────────────────┘6.2 TaskAffinity(任务亲和性)
作用:指定 Activity 所属的任务栈
xml
<application android:taskAffinity="com.example.default">
<!-- 使用默认亲和性(包名) -->
<activity android:name=".MainActivity" />
<!-- 自定义亲和性 -->
<activity
android:name=".ShoppingActivity"
android:taskAffinity="com.example.shopping" />
<!-- 另一个任务栈 -->
<activity
android:name=".SettingsActivity"
android:taskAffinity="com.example.settings" />
</application>6.3 获取任务栈信息
kotlin
class TaskInfoActivity : AppCompatActivity() {
fun getTaskInfo() {
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
// 获取当前任务栈 ID
val taskId = currentTaskId
// 获取任务栈信息(API 21+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val recentTasks = activityManager.recentTasks(20)
for (task in recentTasks) {
Log.d("TaskInfo", "Task ID: ${task.taskId}")
Log.d("TaskInfo", "Root Affinity: ${task.taskInfo?.affinity}")
Log.d("TaskInfo", "Num Activities: ${task.numHistory}")
}
}
// 获取运行的任务
val runningTasks = activityManager.appTasks
Log.d("TaskInfo", "Running tasks: ${runningTasks.size}")
}
}6.4 移动 Activity 到不同栈
kotlin
// 将 Activity 移动到指定任务栈
val intent = Intent(this, TargetActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
taskAffinity = "com.example.newtask"
}
startActivity(intent)
// 从特定任务栈启动
val intent = Intent(this, TargetActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
`package` = "com.example"
}
startActivity(intent)7. Intent Flag 组合使用
7.1 常用 Flag 组合
组合 1:返回首页(清除所有历史)
kotlin
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK
)
}
startActivity(intent)
// 效果:
// 1. 创建新任务栈
// 2. 清除旧任务栈
// 3. 只保留 MainActivity组合 2:SingleTask 复用(清除上方页面)
kotlin
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
startActivity(intent)
// 效果:
// 1. 如果 MainActivity 存在,清除其上方所有 Activity
// 2. 调用 onNewIntent()
// 3. 如果不存在,创建新实例组合 3:单例模式(确保只有一个实例)
kotlin
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP
)
}
startActivity(intent)组合 4:从 Service/Notification 启动
kotlin
val intent = Intent(this, TargetActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)
// 必须添加 FLAG_ACTIVITY_NEW_TASK
// 因为 Service/Notification 不在任务栈中7.2 Flag 优先级
kotlin
// Flag 的优先级(从高到低)
// 1. FLAG_ACTIVITY_MULTIPLE_TASK
// 2. FLAG_ACTIVITY_NEW_TASK
// 3. FLAG_ACTIVITY_CLEAR_TASK
// 4. FLAG_ACTIVITY_CLEAR_TOP
// 5. FLAG_ACTIVITY_SINGLE_TOP
// 6. FLAG_ACTIVITY_PREVIOUS_IS_TOP
// 组合示例
val intent = Intent(this, TargetActivity::class.java).apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or // 新任务栈
Intent.FLAG_ACTIVITY_CLEAR_TOP or // 清除上方
Intent.FLAG_ACTIVITY_SINGLE_TOP // 栈顶复用
)
}8. onNewIntent 处理
8.1 触发时机
onNewIntent() 在以下情况下被调用:
- SingleTop 模式,实例在栈顶被复用
- SingleTask 模式,实例在任意位置被复用
- SingleInstance 模式,实例被复用
8.2 完整示例
kotlin
class MainActivity : AppCompatActivity() {
private var viewPager: ViewPager2? = null
private var adapter: ViewPagerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "onCreate")
// 初始化 UI
initUI()
// 处理初始 Intent
handleIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d("MainActivity", "onNewIntent")
// 更新当前 Intent
setIntent(intent)
// 处理新的 Intent
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
when (intent.action) {
ACTION_SHOW_HOME -> {
// 显示首页
viewPager?.currentItem = 0
}
ACTION_SHOW_SEARCH -> {
// 显示搜索页
viewPager?.currentItem = 1
}
ACTION_SHOW_SETTINGS -> {
// 显示设置页
viewPager?.currentItem = 2
}
}
// 处理传递的数据
val data = intent.getStringExtra("data")
updateUIWith(data)
}
private fun initUI() {
// 初始化界面
}
private fun updateUIWith(data: String?) {
// 更新 UI
}
companion object {
const val ACTION_SHOW_HOME = "com.example.ACTION_SHOW_HOME"
const val ACTION_SHOW_SEARCH = "com.example.ACTION_SHOW_SEARCH"
const val ACTION_SHOW_SETTINGS = "com.example.ACTION_SHOW_SETTINGS"
}
}8.3 与 ViewModel 配合
kotlin
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 观察 ViewModel 的状态
viewModel.uiState.observe(this) { state ->
updateUI(state)
}
// 处理初始 Intent
handleIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
// 通过 ViewModel 更新状态
viewModel.handleIntent(intent)
}
private fun updateUI(state: UIState) {
// 根据状态更新 UI
}
}
class MainViewModel : ViewModel() {
private val _uiState = MutableLiveData<UIState>(UIState.Initial)
val uiState: LiveData<UIState> = _uiState
fun handleIntent(intent: Intent) {
when (intent.action) {
ACTION_SHOW_HOME -> _uiState.value = UIState.Home
ACTION_SHOW_SEARCH -> _uiState.value = UIState.Search
}
}
}9. ASCII 流程图
9.1 Standard 模式流程
┌─────────────────────────────────────────────────┐
│ Standard 模式流程 │
├─────────────────────────────────────────────────┤
│ │
│ 第一次启动: │
│ ┌─────────────┐ │
│ │ MainActivity │ ← onCreate │
│ └─────────────┘ │
│ │
│ 第二次启动: │
│ ┌─────────────┐ │
│ │ MainActivity │ │
│ ├─────────────┤ │
│ │ MainActivity │ ← onCreate(新实例) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────┘9.2 SingleTop 模式流程
┌─────────────────────────────────────────────────┐
│ SingleTop 模式流程 │
├─────────────────────────────────────────────────┤
│ │
│ 场景 1:在栈顶时 │
│ ┌─────────────┐ │
│ │ MainActivity │ ← onNewIntent(复用) │
│ └─────────────┘ │
│ │
│ 场景 2:不在栈顶时 │
│ ┌─────────────┐ │
│ │ DetailActivity│ │
│ ├─────────────┤ │
│ │ MainActivity │ │
│ ├─────────────┤ │
│ │ MainActivity │ ← onCreate(新实例) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────┘9.3 SingleTask 模式流程
┌─────────────────────────────────────────────────┐
│ SingleTask 模式流程 │
├─────────────────────────────────────────────────┤
│ │
│ 初始状态: │
│ ┌─────────────┐ │
│ │ DetailActivity│ │
│ ├─────────────┤ │
│ │ SearchActivity│ │
│ ├─────────────┤ │
│ │ MainActivity │ │
│ └─────────────┘ │
│ │
│ 再次启动 MainActivity: │
│ ↓ │
│ 清除上方的 Activity │
│ ↓ │
│ ┌─────────────┐ │
│ │ MainActivity │ ← onNewIntent(复用) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────┘9.4 SingleInstance 模式流程
┌─────────────────────────────────────────────────┐
│ SingleInstance 模式流程 │
├─────────────────────────────────────────────────┤
│ │
│ Task A Task B (独占) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ MainActivity │ │ PhoneActivity│ │
│ └─────────────┘ └─────────────┘ │
│ │
│ 从 PhoneActivity 打开 DetailActivity: │
│ │
│ Task A Task B (独占) Task C │
│ ┌─────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ MainAct │ │ PhoneActivity│ │ DetailAct │ │
│ └─────────┘ └─────────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────┘9.5 完整导航流程图
┌─────────────────────────────────────────────────────┐
│ 应用完整导航流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 启动应用 │
│ │ │
│ ▼ │
│ ┌─────────────┐ SingleTask │
│ │ MainActivity │ ◀──────────────────────────┐ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ▼ (Standard) │ │
│ ┌─────────────┐ │ │
│ │ SearchAct │ │ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ▼ (Standard) │ │
│ ┌─────────────┐ │ │
│ │ DetailAct │ │ │
│ └─────────────┘ │ │
│ │ │
│ 从通知栏点击返回主页: │ │
│ 清除 SearchAct 和 DetailAct │ │
│ 复用 MainActivity │ │
│ (onNewIntent) │ │
│ │ │
└─────────────────────────────────────────────────────┘10. 面试考点
10.1 基础题
Q1: Android 有哪四种启动模式?
A:
- Standard:默认模式,每次创建新实例
- SingleTop:栈顶复用
- SingleTask:栈内复用
- SingleInstance:独立栈
Q2: 四种启动模式的区别?
A:
- Standard:每次都新建
- SingleTop:仅栈顶时复用
- SingleTask:栈内任意位置复用,清除上方
- SingleInstance:独占栈
Q3: 如何在 Manifest 中设置启动模式?
A:
xml
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />10.2 进阶题
Q4: onNewIntent() 在什么情况下被调用?
A:
- SingleTop 模式,实例在栈顶被复用时
- SingleTask 模式,实例被复用时
- SingleInstance 模式,实例被复用时
Q5: FLAG_ACTIVITY_CLEAR_TOP 的作用?
A: 清除目标 Activity 上方的所有 Activity,配合 SingleTask 使用可实现复用。
Q6: SingleTask 和 SingleInstance 的区别?
A:
- SingleTask:可以在同一栈中有其他 Activity
- SingleInstance:独占一个栈,栈中只有它自己
10.3 高级题
Q7: 如何实现点击通知栏返回首页并清除历史?
A:
kotlin
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK
)
}Q8: TaskAffinity 的作用?
A: 指定 Activity 所属的任务栈,相同 TaskAffinity 的 Activity 会尽量在同一栈中。
Q9: 如何防止 Activity 被多次创建?
A:
- 使用 SingleTask 模式
- 配合 FLAG_ACTIVITY_SINGLE_TOP
- 使用 ActivityResultContracts 管理导航
Q10: 多窗口模式下启动模式的影响?
A: 多窗口模式下,每个窗口可能有独立的任务栈,启动模式会影响窗口间的 Activity 复用。
总结
启动模式和任务栈是 Android 面试的重点:
- Standard:默认,每次新建
- SingleTop:栈顶复用
- SingleTask:栈内复用,清除上方(首页推荐)
- SingleInstance:独占栈(电话/闹钟)
关键 Flag 组合:
NEW_TASK + CLEAR_TASK:返回首页CLEAR_TOP:清除上方页面SINGLE_TOP:栈顶复用
最佳实践:
- 首页使用 SingleTask
- 普通页面使用 Standard
- 特殊功能页面使用 SingleInstance
- 配合 onNewIntent 处理数据更新