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的生命周期:

  1. 创建阶段:调用createSession(),返回一个sessionId
  2. 写入阶段:通过openWrite()把APK数据流式写入临时目录
  3. 提交阶段:调用commit(),触发真正的安装流程
  4. 完成阶段:安装成功或失败,Session被销毁

Session内部维护了一个状态机。我建议你去看一下PackageInstallerSession.java里的mState字段,它定义了十几个状态值。从STATE_IDLESTATE_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 三者的协作关系

好,三个核心类都讲完了。咱们来捋一捋它们是怎么配合的。

说白了,流程是这样的:

  1. 用户点击安装,系统创建PackageInstallerSession,分配一个sessionId
  2. APK数据被写入Session的临时目录
  3. 用户确认安装,Session调用commit()
  4. PMS收到commit请求,开始解析APK、校验签名、检查权限
  5. PMS调用InstallercreateAppData()创建数据目录
  6. Installer通过socket通知installd守护进程执行实际的文件操作
  7. 安装完成,PMS更新packages.xml,发送ACTION_PACKAGE_ADDED广播

你看,这个链条很清晰。Session负责临时存储,PMS负责逻辑判断,Installer负责底层执行。各司其职,互不干扰。

我个人觉得,理解这三者的关系,是掌握安装流程的关键。很多开发者只关注PMS,忽略了Installer和Session的作用。但实际调试时,80%的问题都出在Installer的权限或Session的状态管理上。

嗯,这一章就到这里。下一章咱们会深入installPackageTracedLI这个方法,看看PMS到底是怎么一步步把APK装进去的。