Android 应用程序安装过程源码分析

69 阅读8分钟

核心故事:系统启动时的“包裹”登记大行动​

想象一下,Android 系统就是一个巨大的仓库(设备)。当仓库刚启动时(系统启动),有一个非常重要的部门叫 ​​“包裹登记中心” (PackageManagerService)​​ 就要开始工作了。它的任务就是扫描仓库里几个指定的存放区(特定目录),找到所有写着 .apk 的“包裹”(应用程序安装包),拆开它们,仔细检查里面的“身份证” (AndroidManifest.xml),把包裹里的人和货(应用程序信息、四大组件等)登记到仓库的总目录里。这样,仓库管理员 (ActivityManagerService) 和其他部门 (Launcher 等) 才能知道仓库里有什么“货物”(应用),以及怎么找到和使用它们。

​故事流程与源码对应:​

  1. ​仓库主控室启动 (SystemServer.main)​

    • 仓库的核心管理系统 (SystemServer) 启动了。

    • ​代码体现 (SystemServer.java):​

      java
      Copy
      public static void main(String[] args) {
          init1(args); // 启动初始化的第一阶段 (Native)
          ... // 后续初始化
      }
      
  2. ​呼叫底层设备专家 (SystemServer.init1 -> system_init())​

    • SystemServer 主控室呼叫底层的设备管理专家 (system_init() 函数,在 C++ 层)。

    • 专家先启动一些基础的设备服务(如显示设备 SurfaceFlinger、传感器 SensorService、音频 AudioFlinger 等),确保仓库的基本设施能用。

    • 然后专家说:“基础设备准备好了,现在该启动‘包裹登记中心’这类上层管理系统了!”

    • ​代码体现 (com_android_server_SystemServer.cpp / system_init.cpp):​

      java
      Copy
      // JNI 桥接:SystemServer.init1 -> system_init()
      static void android_server_SystemServer_init1(...) {
          system_init();
      }
      // system_init() 函数
      extern "C" status_t system_init() {
          ... // 启动 SurfaceFlinger, SensorService 等
          AndroidRuntime* runtime = AndroidRuntime::getRuntime();
          runtime->callStatic("com/android/server/SystemServer", "init2"); // 关键!通知 Java 层继续
          ... // 其他处理
      }
      
  3. ​主控室启动管理系统线程 (SystemServer.init2 -> ServerThread)​

    • 收到底层专家的信号,主控室 (SystemServer) 启动了一个名为 ServerThread 的核心管理线程。

    • 这个线程专门负责启动仓库里最重要的几个管理部门,包括我们的主角——“包裹登记中心” (PackageManagerService)。

    • ​代码体现 (SystemServer.java):​

      java
      Copy
      public static final void init2() {
          Thread thr = new ServerThread(); // 创建核心管理线程
          thr.setName("android.server.ServerThread");
          thr.start(); // 启动线程,执行 run()
      }
      
  4. ​核心线程启动“包裹登记中心” (ServerThread.run)​

    • ServerThread 线程开始工作,其中一项重要任务就是创建并启动 PackageManagerService

    • ​代码体现 (SystemServer.java - ServerThread.run):​

      java
      Copy
      public void run() {
          ...
          Slog.i(TAG, "Package Manager"); // 日志:开始启动包管理器
          pm = PackageManagerService.main(context, ...); // 创建并启动 PMS
          ...
          // 注意:这里也会启动 ActivityManagerService (仓库管理员) 等
      }
      
  5. ​“包裹登记中心”诞生 (PackageManagerService.main)​

    • PackageManagerService.main() 方法被调用。

    • 它创建了 PackageManagerService 的核心实例 (new PackageManagerService(...)),并把这个中心注册到仓库的“部门黄页” (ServiceManager) 里,这样其他部门就知道怎么找到它了。

    • ​关键!​​ 在创建 PackageManagerService 实例的 ​​构造函数​​ 里,就开始了扫描和登记包裹的核心工作。

    • ​代码体现 (PackageManagerService.java):​

      java
      Copy
      public static final IPackageManager main(Context context, ...) {
          PackageManagerService m = new PackageManagerService(context, ...); // 创建实例(核心工作在构造里!)
          ServiceManager.addService("package", m); // 注册到“部门黄页”
          return m;
      }
      
  6. ​扫描指定存放区(目录)(PackageManagerService 构造函数)​

    • 新成立的“包裹登记中心” (PackageManagerService) 知道它要去哪些存放区找 .apk 包裹:

      • /system/framework/: 放系统核心框架的“包裹”。
      • /system/app/: 放系统预装应用的“包裹”。
      • /vendor/app/: 放设备制造商预装应用的“包裹”。
      • /data/app/: 放用户自己安装的应用的“包裹”。
      • /data/app-private/: 放有特殊保护(如 DRM)的“包裹”。
    • 中心立刻派出了几个“目录观察员” (AppDirObserver),专门盯着这些存放区,以后如果有新包裹放进来(安装 App)或旧包裹被拿走(卸载 App),观察员能及时报告。

    • 中心马上开始 ​​第一次大扫荡​​:调用 scanDirLI() 函数扫描每个存放区。

    • ​代码体现 (PackageManagerService.java - 构造函数):​

      java
      Copy
      public PackageManagerService(Context context, ...) {
          ...
          // 设置存放区路径
          mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
          mSystemAppDir = new File(Environment.getRootDirectory(), "app");
          mVendorAppDir = new File("/vendor/app");
          mAppInstallDir = new File(Environment.getDataDirectory(), "app");
          mDrmAppPrivateInstallDir = new File(Environment.getDataDirectory(), "app-private");
          ...
          // 创建并启动目录观察员 (OBSERVER_EVENTS 监听文件变化)
          mFrameworkInstallObserver = new AppDirObserver(mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
          mFrameworkInstallObserver.startWatching();
          ... // 对其他目录也创建和启动观察员
          // 核心扫描开始!
          scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM | ..., scanMode, 0);
          scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | ..., scanMode, 0);
          scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM | ..., scanMode, 0);
          scanDirLI(mAppInstallDir, 0, scanMode, 0); // 用户app目录标志不同
          scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode, 0);
          ...
      }
      
  7. ​扫描存放区,检查包裹 (scanDirLI)​

    • scanDirLI() 函数负责扫描一个存放区。

    • 它列出目录下的所有文件。

    • 对每个文件,检查它是不是 .apk 包裹(文件名以 .apk 结尾)。

    • 如果是,就交给“包裹解析专员” (scanPackageLI) 去拆包检查。

    • 如果解析失败(比如包裹损坏),在非系统区 (/data/app) 的包裹会被清理掉(file.delete())。

    • ​代码体现 (PackageManagerService.java):​

      java
      Copy
      private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
          String[] files = dir.list(); // 列出目录下所有文件
          for (int i=0; i<files.length; i++) {
              File file = new File(dir, files[i]);
              if (!isPackageFilename(files[i])) { // 检查文件名是否是*.apk
                  continue; // 不是apk,跳过
              }
              // 是apk!交给专员解析
              PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
              // 如果不是系统区的包裹且解析失败,清理它
              if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && ...) {
                  file.delete(); // 删除损坏的apk文件
              }
          }
      }
      
  8. ​“包裹解析专员”拆包 (scanPackageLI -> PackageParser)​

    • 专员 (scanPackageLI) 拿到一个 .apk 文件。

    • 它请出专业的“包裹解析器” (PackageParser)。

    • 解析器 (PackageParser) 会做两件核心事:

      • ​读取包裹内容:​​ 使用 AssetManager 打开包裹,找到最重要的“身份证”文件 AndroidManifest.xml
      • ​解析身份证:​​ 调用 PackageParser.parsePackage() 解析 AndroidManifest.xml 文件。
    • ​代码体现 (PackageManagerService.java):​

      java
      Copy
      private PackageParser.Package scanPackageLI(File scanFile, ...) {
          ...
          PackageParser pp = new PackageParser(scanFile.getPath()); // 创建专业解析器
          final PackageParser.Package pkg = pp.parsePackage(scanFile, ...); // 解析器开始解析apk
          ...
          // 把解析结果交给下一步处理 (登记入库)
          return scanPackageLI(pkg, ...);
      }
      
  9. ​解析身份证 (PackageParser.parsePackage)​

    • 专业的“包裹解析器” (PackageParser) 开始仔细阅读 AndroidManifest.xml (“身份证”)。

    • 它首先读取包裹的名字 (packageName)。这是包裹的唯一标识!

    • 然后,它逐行扫描身份证上的信息:

      • <application>:​​ 整个应用的信息(如图标、名字、主题、权限)。
      • <activity>:​​ 应用的界面 (UI)。
      • <service>:​​ 应用的后台服务。
      • <receiver>:​​ 应用的广播接收器(接收系统或应用的通知)。
      • <provider>:​​ 应用的数据提供者(允许其他应用共享数据)。
      • <uses-permission>:​​ 应用需要哪些系统权限(访问网络、存储等)。
      • <uses-feature>:​​ 应用需要哪些硬件特性(摄像头、GPS等)。
      • 以及其他标签(如 uses-sdkmeta-data 等)。
    • ​核心解析 (PackageParser.parsePackage 内部):​

      java
      Copy
      private Package parsePackage(...) throws ... {
          ...
          String pkgName = parsePackageName(parser, attrs, flags, outError); // 获取包名
          final Package pkg = new Package(pkgName); // 创建Package对象存放解析结果
          ...
          while ((type = parser.next()) != ...) { // 循环读取XML标签
              String tagName = parser.getName();
              if (tagName.equals("application")) {
                  if (!parseApplication(pkg, res, parser, attrs, flags, outError)) { // 解析<application>标签
                      return null;
                  }
              } else if (tagName.equals("activity")) { // 解析<activity>
                  Activity a = parseActivity(...);
                  pkg.activities.add(a);
              } else if (tagName.equals("receiver")) { // 解析<receiver>
                  Activity a = parseActivity(...); // (Receiver也是Activity的一种)
                  pkg.receivers.add(a);
              } else if (tagName.equals("service")) { // 解析<service>
                  Service s = parseService(...);
                  pkg.services.add(s);
              } else if (tagName.equals("provider")) { // 解析<provider>
                  Provider p = parseProvider(...);
                  pkg.providers.add(p);
              } else if ... // 处理其他标签 (uses-permission, uses-feature等)
          }
          return pkg; // 返回解析好的Package对象
      }
      
  10. ​登记入库 (scanPackageLI 另一版本)​

    • 拿到“包裹解析专员”返回的、包含所有详细信息的 Package 对象。

    • 登记中心开始正式登记:

      • ​登记包裹本身:​​ mPackages.put(pkg.applicationInfo.packageName, pkg) (包名 -> Package对象)。

      • ​登记四大组件:​

        • ​Provider (数据提供者):​​ mProvidersByComponent.put(new ComponentName(p.info.packageName, p.info.name), p) (组件名 -> Provider对象)。
        • ​Service (后台服务):​​ mServices.addService(s) (加入服务解析器)。
        • ​Broadcast Receiver (广播接收器):​​ mReceivers.addActivity(a, "receiver") (加入接收器解析器)。
        • ​Activity (界面):​​ mActivities.addActivity(a, "activity") (加入活动解析器)。
    • 同时,会处理组件的进程名 (fixProcessName),确保它们知道在哪个“工作间”(进程)运行。

    • ​代码体现 (PackageManagerService.java):​

      java
      Copy
      private PackageParser.Package scanPackageLI(PackageParser.Package pkg, ...) {
          synchronized (mPackages) {
              ...
              // 1. 登记包裹本身
              mPackages.put(pkg.applicationInfo.packageName, pkg);
              // 2. 登记Provider
              for (int i = 0; i < pkg.providers.size(); i++) {
                  PackageParser.Provider p = pkg.providers.get(i);
                  p.info.processName = fixProcessName(...); // 修正进程名
                  mProvidersByComponent.put(new ComponentName(p.info.packageName, p.info.name), p);
              }
              // 3. 登记Service
              for (int i = 0; i < pkg.services.size(); i++) {
                  PackageParser.Service s = pkg.services.get(i);
                  s.info.processName = fixProcessName(...);
                  mServices.addService(s); // 加入服务解析器
              }
              // 4. 登记Broadcast Receiver
              for (int i = 0; i < pkg.receivers.size(); i++) {
                  PackageParser.Activity a = pkg.receivers.get(i);
                  a.info.processName = fixProcessName(...);
                  mReceivers.addActivity(a, "receiver"); // 加入接收器解析器
              }
              // 5. 登记Activity
              for (int i = 0; i < pkg.activities.size(); i++) {
                  PackageParser.Activity a = pkg.activities.get(i);
                  a.info.processName = fixProcessName(...);
                  mActivities.addActivity(a, "activity"); // 加入活动解析器
              }
              ...
              return pkg;
          }
      }
      

​故事结局:​

经过这一系列忙碌的操作(系统启动时由 SystemServer 启动 PackageManagerServicePackageManagerService 扫描特定目录,使用 PackageParser 解析 AndroidManifest.xml,并将解析出的应用信息存储在其内部的 mPackagesmActivitiesmServicesmReceiversmProvidersByComponent 等关键集合中),Android 系统就知道了所有安装在 /system/vendor/data/app 等目录下的应用程序的完整信息。

​关键点总结:​

  1. ​时机:​​ 发生在 ​​系统启动过程​​ 中,由 SystemServer 触发。
  2. ​主角:​​ ​PackageManagerService (PMS)​​ 是核心管理者。
  3. ​扫描目标:​​ /system/framework/system/app/vendor/app/data/app/data/app-private 这几个固定目录。
  4. ​核心解析器:​​ ​PackageParser​ 负责解析 APK 文件,重点是读取和解析 AndroidManifest.xml
  5. ​解析内容:​​ ​​包名 (packageName)​​ 是应用的唯一 ID。解析的主要信息包括:​​四大组件 (Activity, Service, Receiver, Provider)​​、​​应用信息 (ApplicationInfo)​​、​​权限声明 (uses-permission)​​、​​特性要求 (uses-feature)​​、​​版本号​​ 等。
  6. ​存储结果:​​ PMS 内部的几个重要集合 (mPackagesmActivitiesmServicesmReceiversmProvidersByComponent) 保存了所有解析出的信息,成为整个系统查询和应用组件解析的基础。
  7. ​后续:​​ 文章结尾提到,虽然应用信息登记好了(安装了),但要在桌面上看到图标,还需要另一个应用 Launcher 从 PMS 这里获取应用列表并展示出来(这是下一篇文章的内容)。

​通俗理解:​​ PackageManagerService 在系统启动时做的“安装”,更像是 ​​应用信息的预加载和注册​​。它解析 APK 的清单文件,把所有重要信息登记在案,为后续应用的启动、组件调用、权限检查等提供依据。它并不是执行用户通常理解的“点击 APK 文件进行安装”的那个过程(虽然核心解析逻辑是类似的),后者通常发生在系统运行后用户主动安装新 App 时。