应用安装:从 PackageInstaller 到提交生效
应用安装:从 PackageInstaller 到提交生效
上一章我们知道了 APK 怎么被解析。这一章回答:一次安装从 pm install 到桌面出现图标, 中间经过哪些阶段,为什么设计成“分阶段提交”?
pm install 隐藏了 PackageInstaller 会话的创建、APK 写入和 commit()。提交后系统还需 完成解析、签名与版本校验、文件落位、必要的 dexopt 以及包表更新,任一阶段失败都不能生效。
commit 才是安装事务的生效点
| 常见误解 | 正确理解 |
|---|---|
| 安装就是把 APK 拷进 /data/app | 拷贝只是中间一步,还要解析、签名校验、dexopt、注册后才提交 |
pm install 一次性完成 | PackageInstaller 是分阶段会话:创建 → 写入 → 提交 |
| 安装失败可以随便重试 | 会话有状态机,失败后要重新创建会话或处理残留 |
核心结论
- 安装主链:创建 Session → 写入 APK → 提交 → 解析/校验 → 拷贝到 /data/app → dexopt → 更新包表与 packages.xml → 广播。
- 提交之前,安装不生效;会话可以分多次写入,适合大 APK 与商店场景。
- 拷贝后的 APK 以“包名 + 随机后缀”目录存放,更新会生成新目录。
- dexopt 为 ART 准备优化代码,首次冷启动速度与之相关。
- 安装完成广播
ACTION_PACKAGE_ADDED/ACTION_PACKAGE_REPLACED,桌面据此刷新。
安装主链
逐步解释:
- 创建会话:调用方声明要装的包名、安装标志,得到 Session。
- 写入 APK:可分多次把 APK 内容写入会话暂存区。
- 提交:会话进入提交状态,PMS 开始真正安装。
- 解析与校验:解析 Manifest、验证签名与权限声明。
- 拷贝:APK 进入
/data/app/<pkg>-<随机后缀>/。 - dexopt:为 ART 准备优化文件,加快首次运行。
- 注册:更新内存包表与 packages.xml,发送广播。
为什么设计成“分阶段”
大 APK 从商店下载可能要几分钟,如果安装必须等完整文件才能开始,用户等待感会很强。 PackageInstaller 把“下载/写入”和“提交校验”分开:文件边下边写,写完再一次性提交。 这也让系统可以在提交前统一校验,避免装到一半发现损坏。
源码证据
阅读目标:确认会话与提交入口。
正文指针:PackageInstallerService.java createSession()/commit(); PackageManagerService.java installStage() 与 scanPackageTracedLI()。
// PackageInstallerService.java(伪代码,行号以 r75 为准)
int createSession(SessionParams params, ...) { ... }
void commit(IntentSender statusReceiver, ...) {
// 校验暂存 APK 并触发 PMS 安装流程
}这段代码证明: 安装的对外入口是会话;提交后才进入 PMS 的安装管线。暂存区数据在 提交前不参与包表,因此“提交前未生效”。
验证与排障
环境:开发设备或模拟器;以下命令不需要 root。
adb push app.apk /data/local/tmp/
adb shell pm install -r /data/local/tmp/app.apk
adb shell dumpsys package <包名>
adb logcat -v threadtime | grep -E "PackageInstaller|PackageManager"pm install 失败时会输出错误码(如 INSTALL_FAILED_INVALID_APK)。dumpsys package 确认是否注册;logcat 里的 PackageInstaller 日志可以看到会话状态流转。先区分“拷贝/校验 失败”还是“注册失败”,再进入对应章节排查。
常见误区
- “安装是拷贝文件这么简单”:解析、签名、dexopt、注册都是安装的一部分。
- “提交前也能生效”:提交前只是暂存,不参与包表。
- “更新会覆盖原目录”:更新生成新目录,旧版本通过回滚机制保留。
- “失败重装就行”:要看会话是否残留、错误码指向哪个阶段。
延伸问题
- 增量安装(incremental install)如何复用 v4 签名与 fs-verity?
INSTALL_FAILED_VERSION_DOWNGRADE在什么场景出现?- 应用商店如何用 Session 实现“边下载边安装”?
- dexopt 失败时安装会成功吗,为什么?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
PackageInstallerService.java | createSession() / commit() | 会话管理 |
PackageManagerService.java | installStage() | 安装管线入口 |
PackageManagerService.java | scanPackageTracedLI() | 安装期扫描注册 |
DexManager.java | dexopt 调度 | ART 优化 |
公共路径:frameworks/base/services/core/java/com/android/server/pm/。行号以 r75 检索为准。
提交阶段如何验证发布者身份,见 签名校验。