3. Intent Filter 配置详解:Android Manifest 中 <intent-filter> 标签、action/category/data 匹配规则、优先级控制

3.1 原理概述

Intent Filter 是 Android 系统声明式导航的核心。组件(Activity、Service、BroadcastReceiver)通过在 AndroidManifest.xml 中声明 <intent-filter>,告知系统自己能够响应哪些隐式 Intent。当应用调用 startActivity() 或 startService() 并传入隐式 Intent 时,PackageManager 会遍历所有已安装应用的 Intent Filter,按照 action、category、data 三重规则进行匹配,最终确定唯一或最佳的组件。若多个组件都匹配成功,则弹出选择器(Disambiguation Dialog)或依据优先级(priority)排序。

核心原则:Intent 必须通过所有声明检查(action 至少匹配一个、category 全部匹配、data 完全匹配)才能被视作候选。未被声明的属性系统不会强制检查,但声明后必须严格匹配。

3.2 匹配规则详解

3.2.1 Action 匹配

  • android:name 指定所支持的 Action 字符串(如 android.intent.action.VIEW)。
  • Intent 中的 Action 可以缺省(null),此时只能匹配未声明任何 Action 的 Filter(极少使用)。
  • Filter 可以声明多个 <action>,Intent 只要匹配其中之一即通过 Action 检查。

3.2.2 Category 匹配

  • Filter 中的每个 <category> 都必须被 Intent 包含(即 Intent 的 Categories 集合是 Filter Categories 的超集)。
  • 隐式 Intent 默认带有 android.intent.category.DEFAULT(由系统自动添加),因此 Filter 必须至少包含 android.intent.category.DEFAULT 才能接收隐式 Intent。
  • 例外:CATEGORY_BROWSABLE 常用于深层链接,允许 Web 浏览器跳转。

3.2.3 Data 匹配

属性 说明 匹配规则
android:scheme URI 协议(如 https、http、content) 必须与 Intent 的 scheme 完全一致(区分大小写)。
android:host URI 主机名(如 example.com) 若声明了 host,则必须精确匹配;支持通配符?不,需精确字符串。推荐使用 *.example.com 模式?不支持,只能声明已知域名。
android:port URI 端口号(如 8080) 仅在 host 存在时生效;省略 port 则匹配任意端口或默认端口。
android:path / android:pathPrefix / android:pathPattern 路径精确匹配、前缀匹配、通配符模式匹配 三者互斥。pathPattern 支持 *(匹配任意字符)和 .*(正则)。
android:mimeType MIME 类型(如 text/plain) 支持通配符 text/*;与 scheme/host 组合使用时需谨慎。

3.2.4 优先级控制

通过 android:priority 属性(整数,值越大优先级越高)控制同类型 Intent 候选组件的排序。优先级仅在多个 Filter 完全匹配且属于同一 Activity 时才参与选择;跨应用时优先级行为可能受系统限制(Android 12+ 默认禁用外部应用的优先级影响,除非用户明确设置默认应用)。

<activity android:name=".DeepLinkActivity"
          android:priority="10">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https"
              android:host="app.example.com"
              android:pathPrefix="/product" />
    </intent-filter>
</activity>
Intent Filter 匹配流程图 发起隐式 Intent 匹配 Action(任一) 匹配 Category(全部必须包含) 匹配 Data(精准/前缀/通配符) 候选组件集合 优先级排序 / 系统选择器 注意:若 Filter 未声明 DEFAULT Category,隐式 Intent 会被系统过滤,导致启动失败。

3.3 实现流程 & 配置示例

  1. 在 AndroidManifest.xml 中为目标组件添加 <intent-filter>:一个组件可声明多个 Filter(例如支持 web 链接和自定义 scheme)。
  2. 至少声明一个 <action>:通常为 android.intent.action.VIEW。
  3. 必须包含 android.intent.category.DEFAULT:否则不在隐式 Intent 候选列表中。
  4. 按需添加 <category android:name="android.intent.category.BROWSABLE" />:使链接可从浏览器直接跳转。
  5. 配置 <data> 元素:指定 scheme、host、path 等。若需要支持多个 domain,重复添加 <data> 标签(注意:<data> 多个标签之间是 OR 关系)。
  6. 设置 android:priority(可选):仅在需要提升自身匹配优先级时使用,注意系统策略变化。
调试技巧:使用 adb 命令 adb shell dumpsys package <package-name> 查看已注册的 Intent Filter 列表。或者使用 adb shell am start -W -a android.intent.action.VIEW -d "https://app.example.com/product/123" 测试深层链接能否正确启动目标 Activity。

3.4 常见错误与避坑

  • 忘记添加 DEFAULT Category:隐式 Intent 自动携带 DEFAULT,Filter 必须显式声明,否则匹配失败。
  • Data 元素未声明 scheme:仅声明 host 或 path 而不带 scheme 会导致匹配不生效(scheme 是必须的顶级属性)。
  • 多个 data 标签混乱:同一 Filter 内多个 <data> 是 OR 关系,但若某个 <data> 只声明了 scheme 而其他 data 声明了 host+path,系统会合并所有声明再做检查,容易产生非预期行为。最佳实践:每个 <intent-filter> 只放一组数据规则;多组规则用多个 filter 分开。
  • priority 在外部应用失效:Android 12+ 默认 Priority 仅在包内生效,跨包优先级通常被忽略,改用 role picker 或系统设置。
  • MIME Type 与 URI 同时声明时的额外限制:若同时声明了 android:mimeType 和 android:scheme,则 filter 只接受符合 scheme 且 MIME type 匹配的 Intent;此时 host/path 若未声明则不检查。
安全警告:切勿在 export 组件的 Intent Filter 中暴露敏感的 Activity(如登录或支付页面)。隐式 Intent 可被任意应用发起,若无权限检查,可能导致意图劫持或信息泄露。建议结合 android:permission 或运行时验证 caller 的签名。

3.5 生产环境配置清单

场景 必需声明 推荐设置
标准 Web 链接(Android App Links) ACTION_VIEW, DEFAULT, BROWSABLE, scheme=http/https, host(已验证域名) 添加 android:autoVerify="true" 开启自动验证
自定义 scheme(如 myapp://) ACTION_VIEW, DEFAULT, BROWSABLE, scheme=myapp 避免使用通用 scheme(如 file、content)
文件打开/分享 ACTION_VIEW 或 ACTION_SEND, mimeType 通配符 配合 FileProvider 或 content:// URI
后台服务启动 自定义 action 字符串(建议使用包名前缀),DEFAULT(但 Android 8+ 隐式启动后台服务受限) 改用 JobIntentService 或显式 Intent

3.6 调试与验证

  • 命令行测试:
    # 测试深层链接(隐式 Intent)
    adb shell am start -a android.intent.action.VIEW -d "https://app.example.com/product/42"
    
    # 查看系统如何处理该 Intent(打印候选 Activity 列表,部分系统需要 root)
    adb shell dumpsys activity a -i | grep "example"
        
  • 使用 Android Studio 的 “App Links Assistant”:配置 URL 映射并自动生成 intent-filter 代码,同时提供调试工具发送测试 intent。
  • 日志检查:在目标 Activity 的 onCreate() 中打印 Intent 数据:
    Log.d("DeepLink", "Action: " + getIntent().getAction());
    Log.d("DeepLink", "Data: " + getIntent().getDataString());
    Log.d("DeepLink", "Categories: " + getIntent().getCategories());
        

3.7 性能与稳定性优化

  • 最小化 Filter 声明:为每个 Activity 只声明必要的 <intent-filter>,避免滥用通配符或过多 action,减少 PackageManager 的匹配计算量。
  • 使用 Android App Links 替代自定义 scheme:自定义 scheme 无法保证唯一性,易被其他应用劫持;App Links 结合 HTTPS 域名的自动验证可提供更强确定性。
  • 合理设置 priority:仅在需要处理同一 Intent 时控制本应用内 Activity 的优先级;对外部应使用 android:autoVerify 和系统默认排序。
  • 避免在 BroadcastReceiver 的 Intent Filter 中动态修改优先级:使用 Context.registerReceiver() 时可通过 IntentFilter.setPriority() 设置运行时优先级,但注意与静态注册的优先级叠加效果复杂,容易引发混乱。