3、核心类解析:PackageManagerService、Installer、PackageInstallerSession
好,咱们进入第三章。这一章我打算把安装流程里三个最核心的类拎出来,好好盘一盘。
你想想看,整个APK安装过程,说白了就是这三兄弟在干活:PackageManagerService(简称PMS)是总指挥,Installer是执行者,PackageInstallerSession是临时工棚。各司其职,缺一不可。
3.1 PackageManagerService —— 安装流程的总司令
PMS这玩意儿,在Android系统里地位极高。它负责管理所有已安装的包信息,也负责处理安装、卸载、更新这些操作。
我个人习惯把PMS比作一个「包管家」。你手机上装了什么App,版本号多少,权限申请了哪些,它全知道。而且它手里握着packages.xml这个文件,每次开机都会读一遍,重建内存里的包信息。
核心职责:
- 解析APK的AndroidManifest.xml
- 检查签名、权限、版本冲突
- 协调Installer完成文件操作
- 更新系统包数据库
- 发送广播通知其他组件
嗯,这里要注意。PMS的安装入口是installStage()方法。它不会直接干活,而是把任务丢给一个Handler线程去处理。为什么?因为安装APK涉及大量I/O操作,不能卡住主线程。
// 这是PMS里的核心安装入口
private void installStage(FileInitArgs args) {
// 检查调用者权限
// 创建PackageInstallerSession
// 丢给Handler异步处理
mHandler.post(() -> {
// 真正的安装逻辑在这里
installPackageTracedLI(args);
});
}
我在项目中遇到过一个问题:某些OEM厂商修改了PMS的安装流程,导致静默安装时签名校验失败。排查了半天,最后发现是他们在installPackageTracedLI里加了一段自定义逻辑。所以你看,PMS虽然是标准组件,但厂商定制时很容易踩坑。
3.2 Installer —— 真正干活的「苦力」
PMS是司令,那Installer就是那个在前线搬砖的士兵。
Installer这个类,其实是一个IPC客户端。它通过socket连接到一个叫installd的守护进程。这个守护进程运行在system用户下,有root权限,能干很多PMS干不了的事。
避坑指南:我曾经以为Installer只是简单的文件拷贝工具。后来才发现,它负责的事情远比你想象的复杂——创建应用数据目录、设置SELinux上下文、分配UID/GID、甚至处理dex2oat优化。这些操作都需要root权限,PMS自己搞不定。
Installer的核心方法就那么几个,我列一下:
| 方法名 | 作用 |
|---|---|
createAppData() |
创建应用的数据目录(/data/data/包名) |
dexopt() |
执行dex优化(把dex转成oat) |
rmPackageDir() |
删除应用目录(卸载时用) |
moveCompleteApp() |
把APK从临时目录移到正式目录 |
你可能会问:为什么PMS不自己创建目录?原因很简单——权限不够。PMS运行在system_server进程里,没有文件系统的完全控制权。而installd是真正的系统守护进程,想怎么操作就怎么操作。
我记得有一次调试一个安装失败的问题,日志里报Failed to create app data。一开始以为是磁盘空间不足,查了半天发现是SELinux策略把installd给拦了。嗯,这种问题在Android 10以后特别常见。
3.3 PackageInstallerSession —— 安装会话的「临时工棚」
这个类,说实话,是Android 5.0以后才引入的。它的出现,解决了以前安装APK时的一个大痛点:大文件安装容易中断。
你想想看,以前安装一个1GB的游戏,如果网络断了或者用户取消了,那之前拷贝的文件全白干了。有了Session机制就不一样了——它把安装过程拆成多个阶段,每个阶段都可以回滚。
Session的生命周期:
- 创建阶段:调用
createSession(),返回一个sessionId - 写入阶段:通过
openWrite()把APK数据流式写入临时目录 - 提交阶段:调用
commit(),触发真正的安装流程 - 完成阶段:安装成功或失败,Session被销毁
Session内部维护了一个状态机。我建议你去看一下PackageInstallerSession.java里的mState字段,它定义了十几个状态值。从STATE_IDLE到STATE_COMMITTED再到STATE_SUCCESS,每一步都有严格的校验。
// Session状态机的一部分
private static final int STATE_IDLE = 0;
private static final int STATE_WRITING = 1;
private static final int STATE_COMMITTED = 2;
private static final int STATE_SUCCESS = 3;
private static final int STATE_FAILED = 4;
private static final int STATE_ABORTED = 5;
这里有个坑,我踩过。Session的commit()方法一旦调用,就不能再写数据了。但如果你在写入阶段就调用了abandon(),那Session会直接进入STATE_ABORTED,所有临时文件都会被清理掉。所以,千万不要在写入过程中误调abandon,否则前功尽弃。
注意事项:Session的临时文件存放在/data/app-staging/目录下。这个目录是系统自动管理的,应用开发者不需要关心。但如果你在调试时发现磁盘空间异常,记得去这个目录看看有没有残留的Session文件。
3.4 三者的协作关系
好,三个核心类都讲完了。咱们来捋一捋它们是怎么配合的。
说白了,流程是这样的:
- 用户点击安装,系统创建PackageInstallerSession,分配一个sessionId
- APK数据被写入Session的临时目录
- 用户确认安装,Session调用
commit() - PMS收到commit请求,开始解析APK、校验签名、检查权限
- PMS调用Installer的
createAppData()创建数据目录 - Installer通过socket通知installd守护进程执行实际的文件操作
- 安装完成,PMS更新
packages.xml,发送ACTION_PACKAGE_ADDED广播
你看,这个链条很清晰。Session负责临时存储,PMS负责逻辑判断,Installer负责底层执行。各司其职,互不干扰。
我个人觉得,理解这三者的关系,是掌握安装流程的关键。很多开发者只关注PMS,忽略了Installer和Session的作用。但实际调试时,80%的问题都出在Installer的权限或Session的状态管理上。
嗯,这一章就到这里。下一章咱们会深入installPackageTracedLI这个方法,看看PMS到底是怎么一步步把APK装进去的。