PMS 是如何安装一个 App 的?

3 阅读4分钟

一、引言:为什么你必须搞懂 App 安装流程?

在日常开发中,我们其实频繁“被动依赖”安装机制,但很少主动理解它:

  • 插件化 / 热修复为什么绕不开安装流程?
  • 为什么有的 APK 安装失败提示 INSTALL_FAILED_*
  • 为什么 split APK / 动态特性安装这么复杂?
  • 为什么有的文件明明存在,却访问不到?

👉 本质问题是:App 的“存在”并不只是文件拷贝,而是系统级注册 + 权限体系接入 + 运行环境准备

这背后的核心角色就是:PMS(PackageManagerService)


二、背景知识:你需要先知道这些

1. PMS 是什么?

PackageManagerService 是 Android 系统中负责:

  • App 安装 / 卸载
  • 包信息管理(PackageInfo)
  • 权限分配
  • 四大组件注册

👉 可以把它理解为:

📦 系统的“应用注册中心 + 包管理数据库”


2. 安装的本质不是 copy

很多人误以为:

安装 = 把 APK 拷贝到 /data/app

但实际上:

✔ 文件落地只是第一步
✔ 更关键的是:

  • 解析 AndroidManifest.xml
  • 注册组件(Activity / Service)
  • 分配 UID
  • 权限校验
  • 写入系统 Package 数据

3. 安装入口

用户点击安装后,入口通常是:

PackageInstaller.Session.commit()

最终会走到:

PackageManagerService.installPackageLI()

三、核心原理解析:安装流程全景图

我们先用一句话总结:

PMS 安装 App = 校验 → 解析 → 拷贝 → 注册 → 持久化

展开来看:


Step 1:安装请求进入 PMS

来源可能是:

  • adb install
  • 应用市场
  • PackageInstaller

最终都会进入:

PackageManagerService#installPackageTracedLI

Step 2:APK 校验(安全第一)

关键校验包括:

✔ 签名校验

  • 新安装:记录签名
  • 升级安装:必须签名一致
compareSignatures()

👉 不一致直接:

INSTALL_FAILED_UPDATE_INCOMPATIBLE

✔ 权限校验

  • 是否请求了系统权限
  • 是否允许安装来源

✔ 完整性校验

  • APK 是否损坏
  • 文件是否合法

Step 3:解析 APK(最关键一步)

PackageParser.parsePackage()

会解析:

  • AndroidManifest.xml
  • 四大组件
  • 权限声明
  • intent-filter

👉 生成核心对象:

PackageParser.Package

💡 类比一下:

APK 就像一份“简历”,PMS 在这里读简历并建立档案


Step 4:dex 优化(编译)

dexopt

作用:

  • 将 dex 编译为 oat / odex
  • 提升运行速度

Android 版本不同:

  • Dalvik:dex → odex
  • ART:AOT / JIT 混合

Step 5:安装目录创建 + 文件拷贝

/data/app/<package>/

包括:

  • base.apk
  • split apk(如果有)
  • lib 目录(native so)

Step 6:分配 UID

Settings.getPackageLPw()

系统会为每个 App 分配:

uid = 10000+

👉 这个 UID 是:

  • Linux 进程隔离的核心
  • 权限隔离的基础

Step 7:注册组件(系统可见)

把解析出来的组件注册到系统:

mActivities
mServices
mReceivers
mProviders

👉 从这一刻开始:

  • Activity 才能被启动
  • Service 才能被绑定

Step 8:更新系统数据(持久化)

关键文件:

/data/system/packages.xml

保存:

  • 包名
  • uid
  • 权限
  • 路径

👉 系统重启后能恢复


Step 9:发送安装完成广播

Intent.ACTION_PACKAGE_ADDED

四、源码关键路径解析

简化调用链:

installPackageTracedLI
    → installPackageLI
        → scanPackageNewLI
            → parsePackage
            → collectCertificates
            → scanPackageDirtyLI
                → register components
                → assign uid

重点函数:

1. parsePackage()

负责:

  • Manifest 解析
  • 组件抽取

2. scanPackageNewLI()

核心逻辑:

  • 注册组件
  • 更新 Settings

3. commitPackageSettings()

写入:

packages.xml

五、实战示例:监听 App 安装

我们可以监听安装广播:

class InstallReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val packageName = intent.data?.schemeSpecificPart
        if (intent.action == Intent.ACTION_PACKAGE_ADDED) {
            println("App installed: $packageName")
        }
    }
}

注册:

<receiver android:name=".InstallReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_ADDED"/>
        <data android:scheme="package"/>
    </intent-filter>
</receiver>

场景:自动初始化 SDK

比如:

  • 统计 SDK
  • 插件加载

可以在安装后触发初始化逻辑


六、常见误区 / 踩坑总结

❌ 误区 1:安装只是拷贝文件

✔ 实际是:

系统级注册行为 + 权限体系接入


❌ 误区 2:APK 解压就能运行

错误!

必须经过:

  • PMS 注册
  • UID 分配

否则无法启动


❌ 误区 3:签名不重要

错误!

👉 签名决定:

  • 是否能升级
  • 是否共享 UID
  • 权限继承

❌ 误区 4:删除文件就等于卸载

rm -rf /data/app/xxx

👉 会导致:

  • 系统记录还在
  • 出现“幽灵应用”

七、性能优化 & 最佳实践

1. 使用 Split APK / App Bundle

减少安装体积:

  • 按需下载
  • 减少 dex 数量

2. 减少 dex 数量

👉 dex 越多:

  • dexopt 越慢
  • 安装时间越长

3. 避免滥用权限

权限越多:

  • PMS 校验越复杂
  • 用户信任下降

4. 合理使用 native 库

只保留必要 ABI:

ndk {
    abiFilters "arm64-v8a"
}

5. 优化首次安装时间

  • 使用 R8 压缩代码
  • 避免大资源文件
  • 减少 class 数量

八、总结:一句话看懂 PMS 安装

最后帮你提炼一个面试级总结

PMS 安装 App 的本质,是把一个 APK 从“文件”转化为“系统认可的应用实体”。

核心过程可以记住 5 步:

  1. 校验(签名 / 权限)
  2. 解析(Manifest → Package)
  3. 拷贝(安装目录)
  4. 注册(组件 + UID)
  5. 持久化(packages.xml)

最后一句话

如果你把 Android 系统想象成一个“操作系统级容器平台”,那么:

PMS 就是它的“应用调度中心”,而安装流程就是“应用上线流程”。