一、 四大启动模式
Q1. LaunchMode 都有哪些?
standard、singleTop、singleTask、singleInstance。
Q2. standard (标准模式) 的特点与坑点?
-
特点 :默认模式。每次启动都创建新实例并压入当前栈顶。
-
坑点 :非 Activity Context(如 Application/Service)启动此模式会崩溃,必须添加 FLAG_ACTIVITY_NEW_TASK 标记。
注:standard 模式的 Activity 默认要进入启动它的那个组件所在的任务栈。
txt
栈状态: [A]
启动 A (standard) -> [A, A(新)]
Q3. singleTop (栈顶复用) 的特点?
- 若已在栈顶 :不创建 新实例,回调
onNewIntent()(onCreate/onStart 不调用)。 - 若不在栈顶:创建新实例压入栈顶。
- 场景:通知栏点击跳转、防止按钮快速连点。
txt
栈状态: [A, B]
启动 B (singleTop) -> [A, B] (复用, onNewIntent)
启动 A (singleTop) -> [A, B, A] (新建, 因为A不在栈顶)
Q4. singleTask (栈内复用) 的特点?
- 独占性:只要栈中存在该实例,多次启动均复用它。
- ClearTop :复用时,自动销毁该实例之上的所有 Activity。
- 栈查找:若找不到所需任务栈 (TaskAffinity),会创建新栈。
- 场景:App 首页、WebView 容器页。
txt
栈状态: [A, B, C] (B为singleTask)
启动 B -> [A, B] (C被销毁, B复用并onNewIntent)
Q5. singleInstance (全局单例) 的特点?
- 加强版 singleTask:整个系统只有这一个实例。
- 独占栈 :该实例只能单独位于一个任务栈中,不允许其他 Activity 入驻。
- 场景:来电界面、闹钟响铃。
ini
Task1: [A, B]
启动 C (singleInstance)
Task1: [A, B] (后台)
Task2: [C] (前台, 独占)
二、 任务栈 (Task) 交互逻辑
Q6. 任务栈如何分类?
- 前台任务栈:用户当前正在交互的栈。
- 后台任务栈:处于后台,其中 Activity 均处于 Paused/Stopped 状态。
Q7. 前台栈 AB,后台栈 CD (均为 singleTask)。启动 D,再按 Back,顺序是什么?
-
启动 D:后台栈 CD 被整体拉回前台,置于 AB 之上。
css[前台栈 Task1] A -> B [后台栈 Task2] C -> D -------- 启动 D -------- [合并视觉] A -> B -> C -> D (Task2 覆盖在 Task1 上) -
Back 顺序 :
D 出栈->显示 C->Back->C 出栈->显示 B->Back->A->桌面。
Q8. A(standard)、B(singleTask)、C(singleTask)。路径: A->B->C->A->B。点两次 Back 看到谁?
结论:回到桌面。
详细推演图解:
less
1. A->B:
栈: [A, B]
2. ->C:
栈: [A, B, C]
3. ->A (standard):
栈: [A, B, C, A] <-- A直接压入
4. ->B (singleTask):
检查栈内... 发现 B!
触发 ClearTop: 销毁 B 之上的 C 和 A
栈: [A, B] <-- B复用
5. Back 1次:
B 出栈 -> 显示 A
栈: [A]
6. Back 2次:
A 出栈 -> 栈空 -> 回到桌面
Q9. TaskAffinity 有什么用?
- 指定 Activity 所需任务栈的名字(默认为包名)。
- 需配合
singleTask或allowTaskReparenting使用才生效。 - 应用:将小程序或工具页放入独立进程/任务栈。
Q10. allowTaskReparenting 是什么?
-
定义:允许 Activity 变更归属栈。
-
现象:
lessApp A (Task A): 启动了 App B 的 Activity C (allowTaskReparenting=true) 状态: Task A [..., C] --> 用户回桌面,点图标启动 App B 现象: C 从 Task A 飞到了 Task B 状态: Task A [...] Task B [C] (C 显示在 B 的栈顶)
三、 启动配置与 Intent
Q11. 如何指定启动模式?优先级如何?
- 静态 :Manifest
android:launchMode。 - 动态 :Intent Flags
intent.addFlags(...)。 - 优先级 :Intent Flags > Manifest。
Q12. 常用的 Intent Flags 解析?
-
FLAG_ACTIVITY_NEW_TASK: 效果近似 singleTask(非 Activity 启动必加)。 -
FLAG_ACTIVITY_SINGLE_TOP: 等同 singleTop。 -
FLAG_ACTIVITY_CLEAR_TOP:- 标准模式下 :销毁目标及之上所有页面,重建目标实例。
- 配合 singleTask :不重建,回调
onNewIntent。
Q13. 显式调用 vs 隐式调用?
- 显式 :指定
package,class name。 - 隐式 :匹配
IntentFilter(Action, Category, Data)。优势是解耦、跨应用。
Q14. IntentFilter 匹配规则 (Action/Category/Data)?
-
Action :Intent 必须有 Action,且必须匹配过滤规则中至少一个。
-
Category :Intent 可无 Category (默认带 DEFAULT)。若有,则必须全部包含在过滤规则中。
- 注:隐式 Activity 必须在 Manifest 声明
android.intent.category.DEFAULT。
- 注:隐式 Activity 必须在 Manifest 声明
-
Data:必须匹配 MIMEType 和 URI。
Q15. 如何防止隐式启动失败崩溃?
使用 PackageManager.resolveActivity() 或 queryIntentActivities(intent, MATCH_DEFAULT_ONLY) 预检查是否返回 null 或空列表。
Q16. Launcher 入口 Activity 必须配置什么?
(缺一不可)
ini
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
四、 实战案例
Q17. 应用每次启动 Acitivity A,都需要保证 A 为栈底。
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!isTaskRoot()) {
Intent intent = getIntent();
String action = intent.getAction();
Intent newIntent = new Intent(this, ActivityA.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(newIntent);
finish();
return;
}
setContentView(R.layout.activity_a);
// ...
}