一、PMS启动流程:系统心脏的第一次跳动

PackageManagerService,简称PMS,是Android系统里最核心的服务之一。说白了,它就是应用的"大管家"——所有APK的安装、卸载、权限管理,都得经过它。

我刚开始研究PMS时,第一反应是:这玩意儿启动得花多久啊?后来在项目中调优启动速度,才真正领教了它的复杂度。

1.1 PMS的启动时机

PMS在SystemServer启动过程中被创建。具体来说,是在SystemServer的startBootstrapServices()方法里调用的。嗯,这里有个细节:PMS的启动分为两个阶段——构造阶段扫描阶段

核心流程:

  1. 创建PMS实例,传入系统上下文
  2. 扫描系统目录(/system/app, /system/priv-app等)
  3. 扫描数据目录(/data/app)
  4. 建立权限数据库
  5. 发布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"但没有配置,结果导致其他应用无法通过隐式Intent启动它。解析时不会报错,但运行时就是找不到组件。所以写AndroidManifest时,exported属性一定要和intent-filter匹配好。

三、权限管理:系统的安全守门员

权限管理是PMS里最复杂的模块之一。我刚开始接触时,被各种权限组、权限级别搞得晕头转向。后来总结了一个规律:权限本质上就是"应用向系统申请的能力清单"

3.1 权限的分类

权限级别 说明 示例
normal 普通权限,安装时自动授予 INTERNET
dangerous 危险权限,需要运行时申请 CAMERA, LOCATION
signature 签名权限,同签名应用自动获取 BIND_ACCESSIBILITY_SERVICE
system 系统权限,仅系统应用可用 INSTALL_PACKAGES

3.2 权限检查机制

当应用调用某个需要权限的API时,系统会通过PMS的checkUidPermission()方法检查。流程是这样的:

  1. 获取调用者的UID
  2. 查询该UID对应的包名列表
  3. 遍历每个包的权限集合
  4. 如果找到匹配的权限,返回PERMISSION_GRANTED
  5. 否则返回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知识结构的。你可以把它当作本章的"地图":

PackageManagerService 启动流程 SystemServer启动 扫描系统目录 扫描数据目录 发布服务 APK解析 PackageParser 解析AndroidManifest 生成Package对象 校验合法性 权限管理 权限分类 权限检查 运行时权限 权限组管理 安装与卸载 拷贝APK 分配UID Dex优化 发送广播

这张图把PMS的四大核心模块串起来了。你想想看,从系统启动到应用安装,每一步都离不开PMS的协调。我个人习惯把PMS比作"Android系统的中枢神经"——它不直接干活,但所有活都得经过它调度。

本章小结:

  • PMS启动分构造和扫描两个阶段,扫描最耗时
  • PackageParser负责把APK的二进制XML解析成Java对象
  • 权限管理分四个级别,dangerous权限需要运行时申请
  • 安装流程涉及拷贝、解析、分配UID、Dex优化、广播通知
  • 卸载时默认不删除用户数据,需要额外处理

公众号:蓝海资料掘金营,微信deep3321