APK 解析:Manifest 如何变成 Package 对象
APK 解析:Manifest 如何变成 Package 对象
上一章我们看到扫描逐个处理 APK。这一章回答:扫描时的“解析”具体做什么, AndroidManifest.xml 怎么变成内存里的 Package 对象?
APK 中的 Manifest 是编译后的二进制 XML。解析器先生成 ParsingPackage,PMS 随后还要 执行校验、状态合并与登记;只有进入各类解析表后,组件查询与 Intent 匹配才有依据。
Manifest 解析只是登记的第一步
| 常见误解 | 正确理解 |
|---|---|
| Manifest 是文本文件,解析像读 JSON | APK 里的 Manifest 是编译后的二进制 XML,解析器要解二进制格式 |
| 解析一次得到的就是最终数据 | 解析出 ParsingPackage 后,PMS 还要复制、校验并登记到各张表 |
| 解析只是“读配置” | 解析决定组件能否被发现、权限能否声明、签名是否可验证 |
核心结论
- 解析入口按 APK 版本选择解析器(Android 10+ 统一走 PackageParser2)。
- 二进制 Manifest 被解码成 ParsingPackage,再复制为系统内部使用的 Package。
- 组件(Activity/Service/Receiver/Provider)、权限声明、SDK 版本、签名都来自解析结果。
- 解析阶段会读取 APK 签名信息,但“验证签名是否正确”发生在安装校验阶段。
- 解析失败的 APK 不会进入包表,并产生可定位的错误码。
解析主链
逐步解释:
- 输入:解析器拿到 APK 文件,读取二进制 Manifest 与签名信息。
- 解码:PackageParser2 解出应用名、版本、SDK、组件、权限等字段。
- 对象化:得到 ParsingPackage,再复制成系统内部稳定的 Package 对象。
- 登记:组件与权限进入 PMS 的各张表,供 AMS 等查询。
组件表为什么重要
AMS 启动 Activity/Service、发送广播时,都要按 Intent 去组件表里找目标。组件表来自解析 结果:Manifest 里没声明的组件,任何进程都无法启动。因此“解析失败”不只是安装失败, 运行期也会表现为“找不到组件”。
源码证据
阅读目标:确认解析器入口与对象生成位置。
正文指针:PackageParser2.java parsePackage();ParsingPackageUtils.java 的解析逻辑; Package.java 作为最终登记对象。
// PackageParser2.java(伪代码,行号以 r75 为准)
ParsingPackage parsePackage(File file, int flags) {
// 解码二进制 Manifest,生成 ParsingPackage
}这段代码证明: 解析的产物先是 ParsingPackage;PMS 后续把它复制为 Package 并登记。 Manifest 里的所有关键信息在这一步进入内存,后续查询全部走这些对象。
验证与排障
环境:开发设备或模拟器;以下命令不需要 root。
adb shell dumpsys package <包名>
adb shell aapt dump badging <apk路径> 2>/dev/null || echo "aapt 不可用"dumpsys package 查看解析后的字段;aapt 可以离线查看 Manifest 摘要用于对照。解析失败的 APK 在 pm install 时会给出 INSTALL_PARSE_FAILED_* 错误码,先在本地用 apkanalyzer 或 aapt 确认 Manifest 是否合法。
常见误区
- “Manifest 是文本”:APK 内是二进制 XML,需解码。
- “解析结果直接可改”:ParsingPackage 是不可变构建产物,系统只读取登记。
- “解析不影响运行期”:组件表来自解析,解析失败则组件不可用。
- “签名在解析时就验证”:解析读取签名信息,验证在安装校验阶段。
延伸问题
ParsingPackage与Package为什么分成两个对象?- 二进制 XML 的字符串池与资源引用如何解析?
- 不同 SDK 版本的 Manifest 特性(如 split、library)如何兼容?
- 解析开销对安装耗时的影响如何优化?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
PackageParser2.java | parsePackage() | 解析入口 |
ParsingPackageUtils.java | 解码逻辑 | Manifest 字段提取 |
Package.java | 对象定义 | 登记用包对象 |
PackageManagerService.java | scanPackageTracedLI() | 扫描与登记调用 |
公共路径:frameworks/base/core/java/android/content/pm/parsing/ 与 frameworks/base/services/core/java/com/android/server/pm/。行号以 r75 检索为准。
解析如何进入安装事务,见 应用安装。