Android LaunchMode

一、 四大启动模式

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,顺序是什么?

  1. 启动 D:后台栈 CD 被整体拉回前台,置于 AB 之上。

    css 复制代码
    [前台栈 Task1] A -> B
    [后台栈 Task2] C -> D
    -------- 启动 D --------
    [合并视觉] A -> B -> C -> D (Task2 覆盖在 Task1 上)
  2. 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 所需任务栈的名字(默认为包名)。
  • 需配合 singleTaskallowTaskReparenting 使用才生效。
  • 应用:将小程序或工具页放入独立进程/任务栈。

Q10. allowTaskReparenting 是什么?

  • 定义:允许 Activity 变更归属栈。

  • 现象

    less 复制代码
    App 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. 如何指定启动模式?优先级如何?

  1. 静态 :Manifest android:launchMode
  2. 动态 :Intent Flags intent.addFlags(...)
  3. 优先级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
  • 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);
    // ... 
}
相关推荐
LYFlied2 小时前
【每日算法】LeetCode 17. 电话号码的字母组合
前端·算法·leetcode·面试·职场和发展
阿里云云原生3 小时前
Android App 崩溃排查实战:如何利用 RUM 完整数据与符号化技术定位问题?
android·阿里云·云原生·rum
过期动态4 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
yaoh.wang5 小时前
力扣(LeetCode) 1: 两数之和 - 解法思路
python·程序人生·算法·leetcode·面试·跳槽·哈希算法
winfield8215 小时前
关于工程实践的面试问题
微服务·面试
yaoh.wang6 小时前
力扣(LeetCode) 27: 移除元素 - 解法思路
python·程序人生·算法·leetcode·面试·职场和发展·双指针
没有了遇见6 小时前
Android 音乐播放器之MotionLayout实现View流畅变换
android
San306 小时前
从零到一:彻底搞定面试高频算法——“列表转树”与“爬楼梯”全解析
javascript·算法·面试
JHC0000006 小时前
118. 杨辉三角
python·算法·面试