13. Intent 解析与 Activity 启动:PMS如何配合AMS解析Intent,找到目标Activity
说实话,Intent 解析这块,我当年刚接触 Android 时也懵过。
明明就是一个“意图”,系统怎么就能精准找到那个 Activity 呢?
这背后其实是 PMS 和 AMS 在唱双簧。今天我们就来拆解这个过程。
13.1 核心角色:PMS 与 AMS 的分工
先理清两个大管家的职责:
- PMS(Package Manager Service):管“静态信息”。它手里有所有已安装应用的 AndroidManifest.xml 数据。说白了,谁注册了哪些 Activity、IntentFilter 长什么样,PMS 门儿清。
- AMS(Activity Manager Service):管“动态调度”。它负责 Activity 的生命周期、任务栈管理。但它不直接存 IntentFilter 数据,得问 PMS 要。
所以流程很清晰:AMS 接到启动请求 → 找 PMS 查 Intent 匹配哪个 Activity → PMS 返回目标 → AMS 去启动它。
关键点:PMS 只负责“查字典”,AMS 负责“执行动作”。两者配合,缺一不可。
13.2 Intent 解析的两种方式
Intent 分两种:显式 Intent 和隐式 Intent。解析逻辑完全不同。
13.2.1 显式 Intent
直接指定了包名和类名。比如:
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);
这种最简单。AMS 拿到 Intent 后,直接根据 ComponentName 去 PMS 查这个类是否存在、是否导出。不存在就抛异常。我在项目中遇到过有人把 Activity 设成了 exported=false,然后跨进程调显式 Intent,结果直接崩溃。嗯,这个坑踩过。
13.2.2 隐式 Intent
没有指定具体类名,只给了 action、category、data 等条件。比如:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://example.com"));
startActivity(intent);
这时候 AMS 就得求助于 PMS 了。PMS 会遍历所有已安装应用的 IntentFilter,做三层匹配:
- action 匹配:Intent 的 action 必须与 IntentFilter 中至少一个 action 相同(或 IntentFilter 没指定 action,但这种情况极少)。
- category 匹配:Intent 中所有 category 都必须在 IntentFilter 的 category 列表里。注意
CATEGORY_DEFAULT是隐式 Intent 默认带上的,所以 IntentFilter 必须声明它。 - data 匹配:包括 scheme、host、port、path、mimeType 等。规则比较复杂,但核心是 Intent 的 data 必须落在 IntentFilter 声明的范围内。
避坑指南:我曾经排查过一个 bug:隐式 Intent 死活调不起某个 Activity。最后发现是 IntentFilter 里只写了 android:scheme="https",但没写 android:host。而 Intent 的 data 带了完整 host。PMS 匹配时要求 scheme 和 host 都得对上。少一个都不行。
13.3 PMS 的匹配流程:从 queryIntentActivities 说起
AMS 调用 PMS 的 queryIntentActivities() 方法。这个方法内部做了啥?
我简化一下核心逻辑:
// 伪代码,非真实源码
List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<>();
for (Package pkg : allPackages) {
for (Activity activity : pkg.activities) {
if (matchIntentFilter(intent, activity.intentFilter)) {
result.add(new ResolveInfo(activity));
}
}
}
// 按优先级排序
Collections.sort(result, priorityComparator);
return result;
}
注意这里有个 优先级排序。如果多个 Activity 都能匹配同一个隐式 Intent,系统会按 priority 属性排序。优先级高的排在前面。如果优先级相同,会弹出选择器让用户选。
你想想看,如果没这个排序,每次打开一个链接都弹一堆选择框,用户体验得多差。
13.4 AMS 的启动流程:拿到目标后的动作
PMS 返回 ResolveInfo 后,AMS 就开始干活了:
- 检查权限:目标 Activity 是否要求调用方有特定权限?没有就抛 SecurityException。
- 检查进程:目标 Activity 所在进程是否已启动?没启动就 fork 新进程。
- 创建 ActivityRecord:AMS 内部用 ActivityRecord 表示一个 Activity 实例。它记录了 Activity 的状态、所在任务栈等信息。
- 处理任务栈:根据 launchMode(standard、singleTop、singleTask、singleInstance)决定是把 Activity 放到现有栈顶,还是新建栈。
- 调度生命周期:通过 ApplicationThread 回调到目标进程,依次执行 onCreate、onStart、onResume。
注意:AMS 在启动 Activity 前,还会调用 PMS 的 checkPermission() 做权限校验。如果目标 Activity 声明了 android:permission,调用方没有该权限,AMS 会直接拒绝启动。我见过有人把系统级权限配在普通 Activity 上,结果第三方应用调它时直接崩溃。嗯,这个坑也不小。
13.5 整体流程图
下面我用一张 SVG 图把整个流程串起来。这样你看起来更直观。
13.6 几个容易踩的坑
最后分享几个我实际项目中遇到的坑:
- 隐式 Intent 没加 CATEGORY_DEFAULT:很多人在 IntentFilter 里只写 action,忘了加
android:name="android.intent.category.DEFAULT"。结果隐式 Intent 永远匹配不到。我排查过两次这种问题。 - data 匹配的 scheme 大小写:PMS 匹配 data 时,scheme 是大小写不敏感的,但 host 是大小写敏感的。嗯,这个细节容易忽略。
- 优先级相同导致选择器弹窗:如果你开发的是系统应用,想默认处理某个链接,记得把 priority 设高一点。否则用户每次都得选。
- exported 属性:Android 12 之后,如果 Activity 没有显式设置
exported,且包含 IntentFilter,系统会强制要求你声明。否则编译都过不了。这个改动我印象很深。
总结一下:Intent 解析本质上是 PMS 的“静态匹配” + AMS 的“动态调度”。PMS 负责查清单,AMS 负责干活。两者配合得好,Activity 才能顺利启动。你想想看,如果没有 PMS 的匹配能力,AMS 面对一个隐式 Intent 根本无从下手。所以理解这个配合关系,比死记硬背代码更重要。
个人习惯:我平时调试 Intent 解析问题时,喜欢用 adb shell dumpsys package 查看 IntentFilter 的注册情况。再配合 adb shell dumpsys activity intents 看 AMS 的解析结果。两个命令一对比,问题基本就定位了。
公众号:蓝海资料掘金营,微信deep3321