Activity 的启动模式|Flags|IntentFilter

以下是 Android 开发中 ​Activity 启动模式 、Flags ​ 及 ​IntentFilter​ 的核心知识点总结,结合官方文档及最佳实践整理:


⚙️ 一、Activity 的四种启动模式(Launch Mode)

  1. ​**standard(标准模式)​**​

    • 特点:每次启动均创建新实例,可存在多个相同 Activity。
    • 任务栈行为:新实例压入启动它的 Activity 所在栈。
    • 注意 :非 Activity 上下文(如 Service)启动时需添加 FLAG_ACTIVITY_NEW_TASK 避免报错。
    • 适用场景:普通页面(如新闻详情页)。
  2. ​**singleTop(栈顶复用模式)​**​

    • 特点 :若目标 Activity 位于栈顶,则复用实例并回调 onNewIntent();否则创建新实例。
    • 任务栈行为:避免栈顶重复创建。
    • 适用场景:通知跳转页、搜索结果页(避免重复打开同一页面)。
  3. ​**singleTask(栈内复用模式)​**​

    • 特点 :全局唯一实例,复用时会清空其上方所有 Activity 并回调 onNewIntent()。
    • 任务栈行为 :默认在启动它的栈中,可通过 taskAffinity 指定新栈(需配合 FLAG_ACTIVITY_NEW_TASK)。
    • 适用场景:应用主界面(如微信首页),确保返回时直接回到根页面。
  4. ​**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,规则如下:

  1. ​Action 匹配​

    • Intent 需包含至少一个 Filter 中声明的 Action(字符串完全一致,区分大小写)。
    • 示例:<action android:name="android.intent.action.SEND"/>。
  2. ​Category 匹配​

    • Intent 中的每个 Category 必须存在于 Filter 中。
    • 未声明 Category 时,系统默认添加 android.intent.category.DEFAULT,因此 Filter 必须包含该 Category。
  3. ​Data 匹配​

    • 包括 MIME 类型 (如 image/*)和 URI(Scheme、Host、Port、Path)。
    • URI 结构:<scheme>://<host>:<port>/<path>(如 http://example.com:80/path)。
    • 注意 :使用 setDataAndType() 同时设置 URI 和 MIME 类型(避免 setData() 和 setType() 互相覆盖)。
  4. ​多 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 不匹配 Scheme alipay。

💎 总结:四种情况匹配规则对比

​配置组合​ ​关键规则​
仅 action 必须显式添加 DEFAULT category
action + category Intent 的 category 必须全匹配,且包含 DEFAULT
action + data data 的 URI 和 MIME 需精确匹配,默认 Scheme 为 content/file
action + category + data 三者需同时满足,任何部分不匹配即失败

✅ 最佳实践建议

  1. 防御性匹配检查 :
    使用 Intent.resolveActivity() 或 PackageManager.queryIntentActivities() 检查是否有匹配组件。
  2. 避免 data 歧义 :
    显式声明 URI 的 scheme 和 host,避免依赖默认值。
  3. 通配符使用 :
    pathPattern 支持 * 通配符(如 image_*.jpg),但需转义为 \*。

可通过命令 adb shell dumpsys activity activities 查看任务栈验证匹配结果。

⚠️ 四、关键注意事项

  1. ​生命周期回调​:

    • 复用 Activity 时(如 singleTop),onNewIntent() 会被调用,需在此方法中更新数据。
  2. ​任务栈调试​:

    • 使用 adb shell dumpsys activity activities 查看栈结构。
  3. ​跨应用共享​:

    • singleInstance + android:exported="true" 允许外部应用调用。
  4. ​隐式启动安全​:

    • 调用前用 resolveActivity() 检查是否存在匹配组件,避免 ActivityNotFoundException。

💡 ​最佳实践​:

  • 主界面使用 singleTask 避免多层返回;
  • 频繁跳转页(如通知入口)用 singleTop 减少重复创建;
  • 谨慎使用 singleInstance,避免内存碎片化。
    完整源码示例及调试技巧可参考:CSDN 启动模式详解。
相关推荐
好的佩奇3 小时前
蓝牙之广播与扫描
android·flutter·蓝牙
传奇开心果编程5 小时前
【Jetpack Compose娓娓道来】第36课:焦点管理与键盘输入——让遥控器、键盘和输入法都“找得到北”
android·kotlin·移动开发·交互·android jetpack·ui框架·声明式ui
hai_android6 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
红尘伴蝶舞6 小时前
从秒开优化到连环崩溃:深入剖析 ViewPager2 在常驻 DialogFragment 中的复用陷阱与自愈方案
android·kotlin
巫山老妖10 小时前
Android 应用架构演进:MVC / MVP / MVVM / MVI (2026)
android·android jetpack
木头左15 小时前
【架构设计】教育类 App 服务端化改造:AI 上移、题库分层与个人题库归属
android·架构设计·系统设计·后端开发·数据库设计
墨狂之逸才15 小时前
USB 的 HOST 和 DEVICE 到底有什么区别?ADB 调试应该选哪个?
android
奶油话梅糖16 小时前
苹果的内核跑上了安卓手机:2,631 Star 的 HakiOS 让 XNU 在 Adreno GPU 上画出 118 帧
android·macos·智能手机
恋猫de小郭16 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
好的佩奇1 天前
Dart 之扩展类型
android·flutter·dart