第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 调试的核心脉络画出来了,建议收藏:
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 里过滤 IntentFilter 或 PackageManager 的日志。系统会打印匹配过程,哪个条件没通过一目了然。比瞎猜快多了。
嗯,Intent 调试这块,说白了就是搞清楚"谁在找谁,条件是什么"。把匹配规则刻在脑子里,遇到问题先查 IntentFilter 配置,再查 PendingIntent 的 flag,最后看深层链接的验证文件。按这个顺序排查,90% 的问题都能解决。