6. APK解析流程(下):解析四大组件、权限、intent-filter 的细节与常见坑

上一章我们把APK解析的骨架讲完了,从Manifest读取到签名校验。今天咱们深入血肉——四大组件、权限声明、还有那个让人又爱又恨的intent-filter。说实话,这部分坑特别多。我早期做系统开发时,好几次被intent-filter的匹配规则搞到头秃。

6.1 四大组件的解析逻辑

PackageManager解析四大组件时,核心工作就是把XML里的标签转化成内存中的数据结构。说白了,就是一顿解析+校验+注册的过程。

6.1.1 Activity的解析

Activity的解析相对简单。系统会读取标签下的所有属性,比如exported、launchMode、configChanges等。嗯,这里要注意:

  • exported:如果没有配置intent-filter,默认是false。我见过不少新手以为不配就是公开的,结果第三方调不动。
  • launchMode:standard、singleTop、singleTask、singleInstance。解析时直接存成整数常量,后续ActivityManager直接读取。
  • configChanges:这个是个位掩码,解析时用"|"组合。我踩过坑——如果漏配orientation,横竖屏切换时Activity会重建,数据全丢。

核心代码片段(简化版):

// 解析Activity标签
ActivityInfo info = new ActivityInfo();
info.name = getAttributeValue(attrs, "android:name");
info.launchMode = parseLaunchMode(attrs);
info.configChanges = parseConfigChanges(attrs);
// 注意:name必须全限定名,如果只写".MainActivity",系统会自动补包名
if (info.name.startsWith(".")) {
    info.name = packageName + info.name;
}

6.1.2 Service与Receiver的解析

Service和Receiver的解析逻辑跟Activity大同小异。但有几个细节我提醒一下:

  • Service:支持android:foregroundServiceType,Android 9之后才引入。解析时如果版本太低,这个字段会被忽略。
  • Receiver:有个坑——android:enabledandroid:exported的默认值。如果Receiver有intent-filter,exported默认是true;没有则是false。我遇到过有人配了intent-filter但忘了配exported,结果第三方应用能调起,自己却调不了,因为系统默认给暴露了。

6.1.3 Provider的解析

ContentProvider的解析稍微复杂点。它多了个authorities属性,这是Provider的唯一标识。解析时系统会做去重检查——同一个authority不能出现在两个Provider里。

注意:authorities可以配多个,用分号分隔。但如果你配了android:authorities="${applicationId}.provider",构建时会替换成真正的包名。解析时系统拿到的已经是替换后的值了。

6.2 权限的解析与校验

权限这块,我个人觉得是APK解析里最容易出bug的地方。系统会解析两类权限:

  1. 自定义权限:通过<permission>标签声明。解析时存入PackageParser.Permission对象。
  2. 请求权限:通过<uses-permission>标签声明。解析时存入PackageParser.PermissionGroup

这里有个经典坑:权限保护级别。系统解析android:protectionLevel时,会校验合法性。如果你写了个不存在的级别,比如signatureOrSystem(这是旧版写法,新版改成了signature|privileged),解析会直接失败。

我的经验:曾经有个项目,自定义权限配了protectionLevel="dangerous",但忘了在Manifest里声明<permission>标签。结果运行时一直报SecurityException。查了半天才发现——权限都没声明,怎么可能生效?

6.3 Intent-Filter的解析细节

Intent-filter的解析,说白了就是三个东西:action、category、data。系统把它们存成IntentFilter对象,后续用于匹配Intent。

6.3.1 Action与Category

Action和Category的解析很简单,就是字符串匹配。但要注意:

  • Action:必须至少配一个。如果没配,这个intent-filter永远不会被匹配到。我见过有人配了个空的intent-filter,然后死活调不起Activity。
  • Category:系统会自动加上android.intent.category.DEFAULT。如果你用startActivity隐式调用,系统会默认加上这个Category。所以如果你的intent-filter没配DEFAULT,隐式调用就匹配不上。

6.3.2 Data的解析

Data标签的解析是最复杂的。它包含:

属性 说明 常见坑
scheme 协议,如http、https、content 如果只配scheme不配host,所有该协议的URI都能匹配
host 主机名 必须跟scheme一起配,否则无效
port 端口号 配了host才能配port
path 精确路径 跟pathPrefix、pathPattern互斥
mimeType MIME类型 可以用通配符,如"image/*"

解析逻辑:系统会把scheme、host、port、path、mimeType分别解析出来,然后做笛卡尔积组合。举个例子:

<data android:scheme="http" android:host="example.com" />
<data android:scheme="https" />
// 这相当于两个intent-filter:
// 1. scheme=http, host=example.com
// 2. scheme=https

6.4 常见坑与避坑指南

这部分我直接说干货,都是我在项目中踩过的:

  • 坑一:intent-filter的优先级——系统解析时不会排序,匹配顺序取决于注册顺序。但实际匹配时,系统会优先匹配更具体的filter。如果你配了两个相似的filter,结果可能不是你想要的。
  • 坑二:权限的maxSdkVersion——从Android 12开始,<uses-permission>支持maxSdkVersion。如果你配了android:maxSdkVersion="30",在Android 12+上这个权限会被忽略。我曾经有个App在Android 13上闪退,就是因为忘了加这个属性,导致系统拒绝了权限请求。
  • 坑三:Provider的grantUriPermissions——如果你配了android:grantUriPermissions="true",但没有配<grant-uri-permission>子标签,系统会默认允许所有URI。这其实是个安全隐患。我建议显式声明允许的URI路径。

特别提醒:Android 11之后,系统对隐式Intent的匹配做了限制。如果你的App targetSdkVersion>=30,并且想通过隐式Intent启动外部Activity,必须在intent-filter里显式声明<queries>。否则系统会直接屏蔽匹配结果。

6.5 知识体系总览

我把本章的核心逻辑画了张图,方便你理解:

APK解析流程(下)核心逻辑 四大组件解析 Activity / Service Receiver / Provider 权限解析 自定义权限 请求权限 Intent-Filter Action / Category Data(scheme/host等) 常见坑:权限保护级别 / Intent-Filter优先级 / maxSdkVersion 输出:PackageParser.Package 对象 解析完成后,四大组件、权限、Intent-Filter全部存入Package对象,供后续安装和运行使用

解析完成后,所有数据都打包进PackageParser.Package对象。这个对象会传给PackageManagerService,用于后续的安装、权限授予、组件调度等操作。

好了,APK解析流程到这里就讲完了。下一章我们聊聊安装流程——从解析完到真正装进系统,中间还有哪些门道。


公众号:蓝海资料掘金营,微信deep3321