以下是 Android 开发中 Activity 启动模式 、Flags 及 IntentFilter 的核心知识点总结,结合官方文档及最佳实践整理:
⚙️ 一、Activity 的四种启动模式(Launch Mode)
-
**
standard
(标准模式)**- 特点:每次启动均创建新实例,可存在多个相同 Activity。
- 任务栈行为:新实例压入启动它的 Activity 所在栈。
- 注意 :非 Activity 上下文(如 Service)启动时需添加
FLAG_ACTIVITY_NEW_TASK
避免报错。 - 适用场景:普通页面(如新闻详情页)。
-
**
singleTop
(栈顶复用模式)**- 特点 :若目标 Activity 位于栈顶,则复用实例并回调
onNewIntent()
;否则创建新实例。 - 任务栈行为:避免栈顶重复创建。
- 适用场景:通知跳转页、搜索结果页(避免重复打开同一页面)。
- 特点 :若目标 Activity 位于栈顶,则复用实例并回调
-
**
singleTask
(栈内复用模式)**- 特点 :全局唯一实例,复用时会清空其上方所有 Activity 并回调
onNewIntent()
。 - 任务栈行为 :默认在启动它的栈中,可通过
taskAffinity
指定新栈(需配合FLAG_ACTIVITY_NEW_TASK
)。 - 适用场景:应用主界面(如微信首页),确保返回时直接回到根页面。
- 特点 :全局唯一实例,复用时会清空其上方所有 Activity 并回调
-
**
singleInstance
(单实例模式)**- 特点:独占一个任务栈,不允许其他 Activity 共存。
- 任务栈行为:跨应用共享实例(如闹钟提醒界面)。
- 注意:过度使用可能导致内存开销增加。
模式 | 实例数量 | 清空栈上 Activity | 独立任务栈 |
---|---|---|---|
standard |
多个 | ❌ | ❌ |
singleTop |
栈顶唯一 | ❌ | ❌ |
singleTask |
全局唯一 | ✅ | 可选 |
singleInstance |
全局唯一 | ✅ | ✅ |
🚩 二、常用 Activity Flags
通过 Intent.addFlags()
动态设置(优先级高于 Manifest 配置):
- **
FLAG_ACTIVITY_NEW_TASK
**
等同于singleTask
,在新栈中启动 Activity(常用于非 Activity 上下文)。 - **
FLAG_ACTIVITY_SINGLE_TOP
**
等同于singleTop
,栈顶复用。 - **
FLAG_ACTIVITY_CLEAR_TOP
**
清空目标 Activity 上方的所有 Activity。若目标非singleTask
模式,会销毁自身并重建新实例。 - **
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
**
不保留在最近任务列表(同android:excludeFromRecents="true"
)。 - **
FLAG_ACTIVITY_NO_HISTORY
**
Activity 退出后自动销毁(如登录跳转页)。
🔍 三、IntentFilter 匹配规则
隐式启动 Activity 需匹配目标组件的 IntentFilter,规则如下:
-
Action 匹配
- Intent 需包含至少一个 Filter 中声明的 Action(字符串完全一致,区分大小写)。
- 示例:
<action android:name="android.intent.action.SEND"/>
。
-
Category 匹配
- Intent 中的每个 Category 必须存在于 Filter 中。
- 未声明 Category 时,系统默认添加
android.intent.category.DEFAULT
,因此 Filter 必须包含该 Category。
-
Data 匹配
- 包括 MIME 类型 (如
image/*
)和 URI(Scheme、Host、Port、Path)。 - URI 结构:
<scheme>://<host>:<port>/<path>
(如http://example.com:80/path
)。 - 注意 :使用
setDataAndType()
同时设置 URI 和 MIME 类型(避免setData()
和setType()
互相覆盖)。
- 包括 MIME 类型 (如
-
多 Filter 处理
Activity 可声明多个
<intent-filter>
,匹配任意一组即可启动。
IntentFilter 的匹配规则由 action
、category
、data
三部分构成,不同组合方式会影响隐式启动 Activity 的匹配逻辑。以下针对 四种常见配置情况 举例说明:
🔧 情况一:仅声明 action
(无 category
和 data
)
规则说明
- **
action
必须存在且匹配**:Intent 需包含与过滤规则中完全一致的 action(区分大小写)。 - **必须添加默认
category
**:即使未声明,系统会自动添加android.intent.category.DEFAULT
,否则无法接收隐式 Intent。
示例代码
xml
<!-- AndroidManifest.xml -->
<activity android:name=".TextActivity">
<intent-filter>
<action android:name="com.example.ACTION_TEXT" />
<category android:name="android.intent.category.DEFAULT" /> <!-- 必须显式添加 -->
</intent-filter>
</activity>
ini
// 匹配的 Intent
Intent intent = new Intent("com.example.ACTION_TEXT");
startActivity(intent);
⚠️ 匹配要点
- 未声明
DEFAULT
category 会导致匹配失败(常见报错:ActivityNotFoundException
)。
🧩 情况二:同时声明 action
和 category
(无 data
)
规则说明
- **
category
需完全覆盖**:Intent 中所有 category 必须能在过滤规则中找到匹配项。 - **
DEFAULT
category 不可省略**:过滤规则中必须包含android.intent.category.DEFAULT
。
示例代码
ini
<activity android:name=".ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.CATEGORY_SHARE" />
</intent-filter>
</activity>
scss
// 匹配的 Intent
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addCategory("com.example.CATEGORY_SHARE");
startActivity(intent);
⚠️ 匹配要点
- Intent 若漏掉
com.example.CATEGORY_SHARE
则匹配失败;若额外添加未声明的 category 也会失败。
🌐 情况三:同时声明 action
和 data
(无显式 category
)
规则说明
- **
data
需严格匹配**:包括 URI 的scheme
、host
、port
、path
及 MIME 类型。 - URI 默认值限制 :未显式声明 URI 时,默认 Scheme 为
content
或file
。
示例代码
ini
<activity android:name=".ImageActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="example.com" android:mimeType="image/*" />
</intent-filter>
</activity>
scss
// 匹配的 Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("https://example.com/photo.jpg"), "image/jpeg");
startActivity(intent);
⚠️ 匹配要点
- **避免使用
setData()
+setType()
**:二者互斥,应用setDataAndType()
。 - 若 URI 为
http://example.com
或 MIME 为text/plain
均不匹配。
⚙️ 情况四:同时声明 action
、category
和 data
规则说明
- 三者必须全部匹配:是最严格的场景,需同时满足 action、category、data 的规则。
示例代码
ini
<activity android:name=".PaymentActivity">
<intent-filter>
<action android:name="com.example.ACTION_PAY" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.CATEGORY_SECURE" />
<data android:scheme="alipay" android:host="payment" />
</intent-filter>
</activity>
scss
// 匹配的 Intent
Intent intent = new Intent("com.example.ACTION_PAY");
intent.addCategory("com.example.CATEGORY_SECURE");
intent.setData(Uri.parse("alipay://payment?order=123"));
startActivity(intent);
⚠️ 匹配要点
- 缺少任意一个 category 或 data 的 Scheme 均失败,例如
http://payment
不匹配 Schemealipay
。
💎 总结:四种情况匹配规则对比
配置组合 | 关键规则 |
---|---|
仅 action |
必须显式添加 DEFAULT category |
action + category |
Intent 的 category 必须全匹配,且包含 DEFAULT |
action + data |
data 的 URI 和 MIME 需精确匹配,默认 Scheme 为 content/file |
action + category + data |
三者需同时满足,任何部分不匹配即失败 |
✅ 最佳实践建议
- 防御性匹配检查 :
使用Intent.resolveActivity()
或PackageManager.queryIntentActivities()
检查是否有匹配组件。 - 避免
data
歧义 :
显式声明 URI 的scheme
和host
,避免依赖默认值。 - 通配符使用 :
pathPattern
支持*
通配符(如image_*.jpg
),但需转义为\*
。
可通过命令
adb shell dumpsys activity activities
查看任务栈验证匹配结果。
⚠️ 四、关键注意事项
-
生命周期回调:
- 复用 Activity 时(如
singleTop
),onNewIntent()
会被调用,需在此方法中更新数据。
- 复用 Activity 时(如
-
任务栈调试:
- 使用
adb shell dumpsys activity activities
查看栈结构。
- 使用
-
跨应用共享:
singleInstance
+android:exported="true"
允许外部应用调用。
-
隐式启动安全:
- 调用前用
resolveActivity()
检查是否存在匹配组件,避免ActivityNotFoundException
。
- 调用前用
💡 最佳实践:
- 主界面使用
singleTask
避免多层返回;- 频繁跳转页(如通知入口)用
singleTop
减少重复创建;- 谨慎使用
singleInstance
,避免内存碎片化。
完整源码示例及调试技巧可参考:CSDN 启动模式详解。