第32章 Intent与IntentFilter调试:隐式Intent、匹配规则、PendingIntent、深层链接

说实话,Intent 这玩意儿,是 Android 的"神经中枢"。我见过太多开发者在隐式 Intent 上翻车,匹配规则记混,PendingIntent 用错 flag,深层链接死活调不起来。今天咱们就把这块彻底捋清楚。

32.1 隐式Intent与显式Intent

显式 Intent 很简单——你直接指定了目标组件的类名。比如:

Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);

隐式 Intent 就不一样了。你不说具体是谁,只说你想要什么。系统根据 IntentFilter 去匹配。说白了,就是"我要发一条短信,谁来帮我处理?"

核心区别:

  • 显式:指定类名,系统直接启动
  • 隐式:指定action/category/data,系统去匹配

我个人习惯,能用显式尽量用显式。隐式 Intent 调试起来太痛苦了,尤其是跨应用调用的时候。

32.2 IntentFilter匹配规则

匹配规则分三块:action、category、data。缺一不可,但规则细节很多人搞混。

32.2.1 action匹配

action 必须完全匹配。注意,是字符串完全相等。Intent 里可以只设一个 action,但 IntentFilter 里可以声明多个。只要匹配其中一个就行。

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <action android:name="android.intent.action.EDIT" />
</intent-filter>

Intent 里设了 ACTION_VIEW,就能匹配上。设了 ACTION_MAIN,就匹配不上。

32.2.2 category匹配

这里有个坑。Intent 里可以不设 category,但系统会自动加上 CATEGORY_DEFAULT。所以你的 IntentFilter 里如果没声明 android.intent.category.DEFAULT,隐式 Intent 就永远匹配不上。

我曾经踩过的坑:写了一个 Activity,IntentFilter 里只配了 action,没配 category。结果用隐式 Intent 死活调不起来。查了半天,原来是少了 CATEGORY_DEFAULT

规则总结:

  • Intent 里所有 category 必须全部在 IntentFilter 中找到匹配
  • IntentFilter 可以声明更多 category,没关系
  • 但 Intent 里不能有 IntentFilter 没有的 category

32.2.3 data匹配

data 包括 URI 和 MIME type。匹配规则稍微复杂:

Intent 设置 IntentFilter 设置 匹配结果
URI + MIME URI + MIME 两者都匹配
仅 URI URI + MIME 不匹配(缺 MIME)
仅 MIME URI + MIME 不匹配(缺 URI)
URI + MIME 仅 URI 不匹配(缺 MIME)

嗯,这里要注意:如果 IntentFilter 里声明了 data,那 Intent 必须同时提供 URI 和 MIME type 才能匹配。除非 IntentFilter 里只声明了 scheme 之类的部分信息。

32.3 PendingIntent调试

PendingIntent 是个"令牌"。你把 Intent 包装成 PendingIntent 交给别人,别人拿着这个令牌可以帮你启动组件。常见于通知、AppWidget、AlarmManager。

调试 PendingIntent 时,我最常遇到的问题是 flag 用错。

// 错误示例:每次都创建新的 PendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(
    context, 0, intent, PendingIntent.FLAG_IMMUTABLE);

// 正确示例:使用 FLAG_UPDATE_CURRENT 更新已有
PendingIntent pendingIntent = PendingIntent.getActivity(
    context, 0, intent, 
    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);

调试技巧:adb shell dumpsys activity intents 可以查看当前系统中所有的 PendingIntent。我曾经靠这个命令定位到一个通知点击没反应的 bug——原来是 requestCode 重复导致 PendingIntent 被覆盖了。

32.4 深层链接(Deep Link)

深层链接说白了就是通过 URL 直接打开 App 的某个页面。分两种:

  • URL Scheme: 比如 myapp://product/123
  • App Links: 比如 https://www.myapp.com/product/123

32.4.1 URL Scheme配置

<activity android:name=".ProductActivity">
    <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="myapp"
            android:host="product"
            android:pathPrefix="/" />
    </intent-filter>
</activity>

注意 BROWSABLE 这个 category。不加的话,浏览器里点链接打不开你的 App。

32.4.2 App Links验证

App Links 比 Scheme 更安全,因为需要验证域名所有权。验证文件要放在 https://yourdomain.com/.well-known/assetlinks.json

调试时可以用这个命令:

adb shell dumpsys package domain-preferred-apps

看看你的 App 有没有被系统标记为"默认处理该域名的应用"。

我曾经踩过的坑:App Links 配置好了,但点击链接还是弹出选择器。查了半天,发现是 assetlinks.json 文件里 sha256_cert_fingerprints 写错了。注意,这个指纹是签名证书的,不是 debug.keystore 的。

32.5 知识体系总览

下面这张图把 Intent 调试的核心脉络画出来了,建议收藏:

Intent 调试 隐式 Intent action 完全匹配 category 必须含 DEFAULT data URI + MIME 联合匹配 PendingIntent FLAG_IMMUTABLE / MUTABLE FLAG_UPDATE_CURRENT requestCode 唯一性 深层链接 URL Scheme 配置 App Links 验证 assetlinks.json 调试工具:dumpsys / logcat / adb 核心原则:IntentFilter 匹配是"与"逻辑,缺一不可

32.6 调试实战技巧

最后分享几个我常用的调试命令:

# 查看 Activity 的 IntentFilter
adb shell dumpsys package com.example.app | grep -A 20 "Intent Filter"

# 测试隐式 Intent 能否匹配
adb shell am start -a android.intent.action.VIEW -d "myapp://product/123"

# 查看 PendingIntent 列表
adb shell dumpsys activity intents

# 查看 App Links 验证状态
adb shell dumpsys package domain-preferred-apps

个人经验:调试隐式 Intent 时,我习惯先在 logcat 里过滤 IntentFilterPackageManager 的日志。系统会打印匹配过程,哪个条件没通过一目了然。比瞎猜快多了。

嗯,Intent 调试这块,说白了就是搞清楚"谁在找谁,条件是什么"。把匹配规则刻在脑子里,遇到问题先查 IntentFilter 配置,再查 PendingIntent 的 flag,最后看深层链接的验证文件。按这个顺序排查,90% 的问题都能解决。

专注资料整理