一、PMS启动流程:系统心脏的第一次跳动
PackageManagerService,简称PMS,是Android系统里最核心的服务之一。说白了,它就是应用的"大管家"——所有APK的安装、卸载、权限管理,都得经过它。
我刚开始研究PMS时,第一反应是:这玩意儿启动得花多久啊?后来在项目中调优启动速度,才真正领教了它的复杂度。
1.1 PMS的启动时机
PMS在SystemServer启动过程中被创建。具体来说,是在SystemServer的startBootstrapServices()方法里调用的。嗯,这里有个细节:PMS的启动分为两个阶段——构造阶段和扫描阶段。
核心流程:
- 创建PMS实例,传入系统上下文
- 扫描系统目录(/system/app, /system/priv-app等)
- 扫描数据目录(/data/app)
- 建立权限数据库
- 发布PMS服务到ServiceManager
// 伪代码:PMS启动核心
public PackageManagerService(Context context) {
// 1. 创建Settings对象,管理包信息
mSettings = new Settings();
// 2. 扫描系统包
scanDirLI(systemAppDir, ...);
// 3. 扫描用户安装的包
scanDirLI(dataAppDir, ...);
// 4. 更新权限
mSettings.updatePermissions();
}
1.2 扫描阶段的关键点
扫描阶段其实是最耗时的。我记得有一次线上问题,手机开机后桌面一直黑屏,查了半天发现是某个三方APK的AndroidManifest.xml格式异常,导致PMS扫描卡住了。所以这里我建议:系统预置应用一定要做严格的格式校验。
扫描时,PMS会为每个APK创建一个PackageParser.Package对象,里面包含了应用的所有元数据——包名、版本号、权限声明、四大组件信息等等。
二、APK解析:PackageParser的解剖学
APK本质上是个ZIP包,但Android系统怎么读懂它?靠的就是PackageParser。这个类负责把APK里的AndroidManifest.xml解析成内存中的数据结构。
2.1 解析流程
解析过程大致分三步:
- 第一步:读取APK的二进制AndroidManifest.xml
- 第二步:解析标签,生成
Package对象 - 第三步:校验合法性(比如包名是否为空、版本号是否合法)
// PackageParser核心方法
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
// 解析AndroidManifest.xml
parseBaseApk(packageFile, ...);
// 解析各个组件
parseInstrumentation(owner, resources, parser, ...);
parseApplication(owner, resources, parser, ...);
return owner;
}
你想想看,一个APK里可能有几十个Activity、Service、Receiver,每个都要解析并注册到系统里。这就是为什么安装大应用时会感觉"正在优化应用"。
避坑指南:我曾经遇到一个案例,APK里声明了android:exported="true"但没有配置
三、权限管理:系统的安全守门员
权限管理是PMS里最复杂的模块之一。我刚开始接触时,被各种权限组、权限级别搞得晕头转向。后来总结了一个规律:权限本质上就是"应用向系统申请的能力清单"。
3.1 权限的分类
| 权限级别 | 说明 | 示例 |
|---|---|---|
| normal | 普通权限,安装时自动授予 | INTERNET |
| dangerous | 危险权限,需要运行时申请 | CAMERA, LOCATION |
| signature | 签名权限,同签名应用自动获取 | BIND_ACCESSIBILITY_SERVICE |
| system | 系统权限,仅系统应用可用 | INSTALL_PACKAGES |
3.2 权限检查机制
当应用调用某个需要权限的API时,系统会通过PMS的checkUidPermission()方法检查。流程是这样的:
- 获取调用者的UID
- 查询该UID对应的包名列表
- 遍历每个包的权限集合
- 如果找到匹配的权限,返回PERMISSION_GRANTED
- 否则返回PERMISSION_DENIED
// 权限检查核心逻辑
public int checkUidPermission(String permName, int uid) {
// 获取UID对应的所有包
String[] pkgNames = getPackagesForUid(uid);
for (String pkgName : pkgNames) {
// 检查每个包的权限状态
if (mSettings.isPermissionGranted(pkgName, permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
return PackageManager.PERMISSION_DENIED;
}
注意:Android 11之后,权限管理更加严格。比如QUERY_ALL_PACKAGES权限,普通应用基本拿不到。我有个项目做应用市场,就因为这个权限被拒了好几次。解决方案是改用PackageManager.queryIntentActivities()按需查询。
四、应用安装与卸载:PMS的日常操作
安装和卸载,是PMS最频繁的操作。每次你在应用商店点"安装",背后都是一套复杂的流程。
4.1 安装流程
安装过程可以概括为:
- 拷贝APK:将APK文件复制到/data/app/目录下
- 解析APK:调用PackageParser解析AndroidManifest
- 分配UID:为应用分配Linux用户ID
- 提取Dex:将Dex文件优化为Odex(Android 5.0之前是DexOpt)
- 更新权限:将声明的权限写入系统数据库
- 发送广播:通知其他应用"有新应用安装了"
// 安装核心方法(简化版)
public void installPackage(File apkFile, ...) {
// 1. 拷贝APK
copyApk(apkFile, installDir);
// 2. 解析APK
PackageParser.Package pkg = parseApk(apkFile);
// 3. 分配UID
int uid = mSettings.getOrCreateUserId(pkg.packageName);
// 4. 提取并优化Dex
performDexOpt(pkg);
// 5. 更新权限数据库
mSettings.updatePermissions(pkg);
// 6. 发送安装完成广播
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, pkg);
}
4.2 卸载流程
卸载相对简单,但有个坑:卸载时不会立即删除/data/data/下的用户数据。这是为了防止用户误操作后数据丢失。只有调用deleteApplicationCacheFiles()才会清理。
我曾经遇到一个bug:用户卸载了应用,但/data/data/下残留了大量文件,导致存储空间不足。后来在PMS的卸载逻辑里加了个判断——如果应用是"完全卸载",就一并删除数据目录。
五、知识体系总览
下面这张图,是我个人习惯用来梳理PMS知识结构的。你可以把它当作本章的"地图":
这张图把PMS的四大核心模块串起来了。你想想看,从系统启动到应用安装,每一步都离不开PMS的协调。我个人习惯把PMS比作"Android系统的中枢神经"——它不直接干活,但所有活都得经过它调度。
本章小结:
- PMS启动分构造和扫描两个阶段,扫描最耗时
- PackageParser负责把APK的二进制XML解析成Java对象
- 权限管理分四个级别,dangerous权限需要运行时申请
- 安装流程涉及拷贝、解析、分配UID、Dex优化、广播通知
- 卸载时默认不删除用户数据,需要额外处理