文章内容
- 3.1 Lifecycle 包装层和依赖注入
- 3.2 构造函数建立运行骨架
- 3.3 start() 与电源管理
- 4.1 ready 回调为什么位于启动尾部
- 4.2 SystemUI 的显式启动
- 4.3 HOME Intent 如何变成桌面 Activity
- 5.1 Settings 建立持久化边界
- 5.2 共享 UID 与 PMS 初始化
- 5.3 dexopt、工作线程与目录
- 6.1 scanDirLI 的筛选与失败处理
- 6.2 scanPackageLI 进入解析器
- 7.1 单 APK 与拆分包
- 7.2 APK 如何进入 AssetManager
- 7.3 Manifest 属性节点
- 7.4 application 与组件公共字段
- 7.5 Activity、launchMode 与 taskAffinity
- 7.6 manifest示例解析
素材来源于“AMS 的启动过程分析”“PMS 的启动过程分析”“安装包的解析过程分析”三段视频。
一、system_server
应用进程里的 Application.onCreate() 已经处于启动链后段。此时系统早已知道包名对应哪个 UID、入口 Activity 是谁、它声明了哪些 intent-filter。这些数据若到 startActivity 时才从 APK 临时读取,每次组件解析都要重复访问压缩包和 XML,系统也无法在开机阶段建立统一的包状态。
因此,观察范围需要移到 system_server。它承载 AMS、PMS 等系统服务,开机主线程还要处理服务依赖、Binder 发布、目录扫描和 ready 阶段。线程等待、锁竞争、跨进程调用以及扫描期 I/O 都可能影响系统进入可交互状态而且应用侧的 onCreate() 优化无法覆盖这些耗时。

SystemServer.main() 创建实例并进入 run(),run() 按引导服务、核心服务、其他服务三个阶段组织启动,随后让主线程进入消息循环。下面这段代码保留了这条外层控制链。
// 进程入口接收启动参数。public static void main(String[] args) { // 创建 SystemServer 并进入主流程。 new SystemServer().run();// 结束进程入口。} // run 负责组织系统服务启动阶段。private void run() { // 引导服务包含 Installer、AMS 和 PMS。 startBootstrapServices(); // 核心服务在引导服务之后启动。 startCoreServices(); // 其余系统服务在最后一个阶段启动。 startOtherServices(); // 主线程进入消息循环。 Looper.loop(); // Looper 意外退出时终止启动流程。 throw new RuntimeException("Main thread loop unexpectedly exited");// 结束 run。}二、system_server 初始化
startBootstrapServices() 处理彼此存在早期依赖的服务。Installer 需要赶在其他服务之前连接 installd,以便准备 /data/user 等目录;AMS 随后通过 ActivityManagerService.Lifecycle 创建。
PowerManagerService 和默认显示准备完成后,vold.decrypt 决定 mOnlyCore,PMS 才进入构造与扫描。

下面这段代码对应 SystemServer.java:322-367,它的输入包括 mSystemContext、Installer、工厂测试状态和 mOnlyCore,输出是保存于 SystemServer 字段中的 AMS、PMS 实例。
// 定义引导服务启动阶段。private void startBootstrapServices() { // 启动 Installer 并取得实例。 Installer installer = mSystemServiceManager.startService(Installer.class); // 通过 Lifecycle 创建并取得 AMS 本体。 mActivityManagerService = mSystemServiceManager.startService( // 指定 AMS 的 SystemService 包装类。 ActivityManagerService.Lifecycle.class).getService(); // 把 SystemServiceManager 交给 AMS。 mActivityManagerService.setSystemServiceManager(mSystemServiceManager); // 把 Installer 交给 AMS。 mActivityManagerService.setInstaller(installer); // 读取设备加密启动状态。 String cryptState = SystemProperties.get("vold.decrypt"); // 加密进行中时限制为 core app。 if (ENCRYPTING_STATE.equals(cryptState)) { // 记录限制扫描的原因。 Slog.w(TAG, "Detected encryption in progress - only parsing core apps"); // 开启 core-only 模式。 mOnlyCore = true; // 已加密设备也采用 core-only 模式。 } else if (ENCRYPTED_STATE.equals(cryptState)) { // 记录设备已加密状态。 Slog.w(TAG, "Device encrypted - only parsing core apps"); // 开启 core-only 模式。 mOnlyCore = true; // 结束加密状态判断。 } // 记录 PMS 启动节点。 Slog.i(TAG, "Package Manager"); // 创建 PMS,并把扫描模式传入。 mPackageManagerService = PackageManagerService.main(mSystemContext, installer, // 传入工厂测试标志与 core-only 标志。 mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); // 保存 PMS 判断出的首次启动状态。 mFirstBoot = mPackageManagerService.isFirstBoot(); // 取得面向 Context 的 PackageManager 门面。 mPackageManager = mSystemContext.getPackageManager();// 结束引导阶段。}mOnlyCore 是字段名,其值来自 vold.decrypt。加密流程进行中或设备处于相应加密状态时,PMS 限制包解析范围,避免在此阶段处理普通第三方应用。
三、AMS 是怎么创建出来的
3.1 Lifecycle 包装层和依赖注入

SystemServiceManager.startService(ActivityManagerService.Lifecycle.class) 操作的是符合 SystemService 生命周期的包装层。
下面这段代码中 PMS 走另一种入口:构造完成后以 package 为服务名注册到 ServiceManager。
// PMS 的静态入口接收启动所需依赖与模式。public static PackageManagerService main(Context context, Installer installer, // 声明工厂测试与 core-only 参数。 boolean factoryTest, boolean onlyCore) { // PMS 构造函数完成设置读取、扫描与解析。 PackageManagerService m = new PackageManagerService(context, installer, // 将两个模式参数传入构造函数。 factoryTest, onlyCore); // 发布名为 package 的 Binder 服务。 ServiceManager.addService("package", m); // 返回同一个 PMS 实例。 return m;// 结束 PMS 静态入口。}下面这段代码展示 Lifecycle 的职责边界。构造器创建 AMS 本体;SystemServiceManager 调用 onStart() 时,Lifecycle 才转入 AMS 的 start()。
// Lifecycle 接入统一的 SystemService 生命周期。public static final class Lifecycle extends SystemService { // 保存 AMS 本体。 private final ActivityManagerService mService; // Lifecycle 构造器接收系统上下文。 public Lifecycle(Context context) { // 初始化 SystemService 基类。 super(context); // 创建 AMS 本体。 mService = new ActivityManagerService(context); // 结束 Lifecycle 构造器。 } // 声明启动阶段回调。 @Override // SystemServiceManager 调用该方法启动 AMS。 public void onStart() { // 进入 AMS 自身的 start。 mService.start(); // 结束启动回调。 } // 向 SystemServer 暴露 AMS 实例。 public ActivityManagerService getService() { // 返回构造出的服务本体。 return mService; // 结束实例访问方法。 }// 结束 Lifecycle 定义。}3.2 构造函数建立运行骨架
下面这段代码是构造函数前半段。AMS 新建一条前台优先级 ServiceThread,MainHandler 使用该线程的 Looper;UiHandler 在这个版本的实现中也由 AMS 构造。两个 BroadcastQueue 分别承载前台与后台广播,ActiveServices、ProviderMap 则保存服务和 Provider 管理状态。
// AMS 构造器接收 system_server 的 Context。public ActivityManagerService(Context systemContext) { // 保存系统上下文。 mContext = systemContext; // 读取工厂测试模式。 mFactoryTest = FactoryTest.getMode(); // 保存当前系统 ActivityThread。 mSystemThread = ActivityThread.currentActivityThread(); // 创建 AMS 专用 ServiceThread。 mHandlerThread = new ServiceThread(TAG, // 设置前台线程优先级并禁止常规 I/O。 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); // 启动 ServiceThread。 mHandlerThread.start(); // 将 MainHandler 绑定到 ServiceThread 的 Looper。 mHandler = new MainHandler(mHandlerThread.getLooper()); // 创建处理 UI 相关消息的 UiHandler。 mUiHandler = new UiHandler(); // 创建前台广播队列。 mFgBroadcastQueue = new BroadcastQueue(this, mHandler, // 使用前台广播超时参数。 "foreground", BROADCAST_FG_TIMEOUT, false); // 创建后台广播队列。 mBgBroadcastQueue = new BroadcastQueue(this, mHandler, // 使用后台广播超时参数。 "background", BROADCAST_BG_TIMEOUT, true); // 建立 Service 管理对象。 mServices = new ActiveServices(this); // 建立 Provider 映射。 mProviderMap = new ProviderMap(this);// 省略构造函数后续对象初始化。}
下面这段代码补上 Activity 栈、最近任务、任务持久化和 CPU 采样线程之间的联系。ActivityStackSupervisor 管理 ActivityStack、TaskRecord 与 ActivityRecord;mProcessCpuThread 在构造阶段创建,启动动作留给 start()。
// 创建最近任务管理对象。mRecentTasks = new RecentTasks(this);// 让 ActivityStackSupervisor 持有 AMS 与最近任务。mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks);// 创建任务持久化对象。mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks);// 创建 CPU 采样线程。mProcessCpuThread = new Thread("CpuTracker") { // 覆盖线程执行入口。 @Override // 定义采样循环。 public void run() { // 线程持续进行周期性采样。 while (true) { // 实际源码在此计算延迟并更新 CPU 统计。 updateCpuStatsNow(); // 结束采样循环。 } // 结束线程入口。 }// 结束线程对象并保存到字段。};3.3 start() 与电源管理
下面这段代码中,两个 setter 完成依赖注入,start() 清理继承自 zygote 的进程组状态、启动 CPU 线程、发布 BatteryStats 与 AppOps,并把 ActivityManagerInternal 放入 LocalServices。AMS 的 Activity Binder 注册不在该方法内;SystemServer 稍后调用 setSystemProcess() 完成相关注册。setSystemProcess() 里做的是 ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true),外加 processinfo、permission、meminfo、gfxinfo、dbinfo、cpuinfo 这几个调试与统计接口,最后把 system_server 自己作为一个持久进程登记进 AMS 的进程表。
// 注入 SystemServiceManager。public void setSystemServiceManager(SystemServiceManager mgr) { // 保存管理器引用。 mSystemServiceManager = mgr;// 结束 setter。}// 注入 Installer。public void setInstaller(Installer installer) { // 保存 Installer 引用。 mInstaller = installer;// 结束 setter。}// 启动 AMS 的运行期设施。private void start() { // 清理 zygote 遗留的进程组设置。 Process.removeAllProcessGroups(); // 启动构造阶段创建的 CPU 线程。 mProcessCpuThread.start(); // 发布 BatteryStats 服务。 mBatteryStatsService.publish(mContext); // 发布 AppOps 服务。 mAppOpsService.publish(mContext); // 注册 system_server 内部接口。 LocalServices.addService(ActivityManagerInternal.class, new LocalService());// 结束 AMS 启动方法。}下面这段代码在 PowerManagerService 启动后执行。它初始化栈和电池统计的电源管理能力,并创建语音场景使用的部分唤醒锁。
// 初始化 AMS 依赖的电源管理对象。public void initPowerManagement() { // 初始化 ActivityStackSupervisor 的电源状态。 mStackSupervisor.initPowerManagement(); // 初始化 BatteryStatsService 的电源状态。 mBatteryStatsService.initPowerManagement(); // 取得 system_server 内部的 PowerManager 接口。 mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); // 从 Context 取得公开 PowerManager。 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); // 创建语音功能使用的部分唤醒锁。 mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*"); // 关闭该唤醒锁的引用计数。 mVoiceWakeLock.setReferenceCounted(false);// 结束电源管理初始化。}四、AMS 的收尾:systemReady 拉起桌面和 SystemUI
4.1 ready 回调为什么位于启动尾部
startOtherServices() 已经建立窗口、输入等服务后,才调用 mActivityManagerService.systemReady(runnable)。AMS 在 systemReady() 内整理进程和用户状态,并在启动初始应用前执行回调。回调把 SystemService 推进到 PHASE_ACTIVITY_MANAGER_READY,准备 WebView,再启动 SystemUI。过早调用会让第三方代码或初始应用面对尚未 ready 的依赖服务。

SystemServer 把收尾任务交给 AMS,AMS 达到允许初始应用启动的状态后回调 SystemServer。
// 告知 AMS 系统服务已接近可运行状态。mActivityManagerService.systemReady(new Runnable() { // 声明 ready 回调入口。 @Override // 执行 SystemServer 的剩余准备工作。 public void run() { // 将 SystemService 推进到 AMS ready 阶段。 mSystemServiceManager.startBootPhase( // 使用 Activity Manager Ready 阶段常量。 SystemService.PHASE_ACTIVITY_MANAGER_READY); // 在 system_server 中准备 WebView。 WebViewFactory.prepareWebViewInSystemServer(); // 启动 SystemUI 服务。 startSystemUi(context); // 结束 ready 回调。 }// 结束 systemReady 调用。});4.2 SystemUI 的显式启动
下面这段代码用显式 ComponentName 指定 com.android.systemui.SystemUIService,并以 user 0 的 UserHandle.OWNER 身份调用 startServiceAsUser()。状态栏、导航栏、通知界面、近期任务界面和锁屏相关界面属于 SystemUI 在该版本中的主要界面职责;具体模块会随 Android 版本调整。
// 定义 SystemUI 启动辅助方法。static final void startSystemUi(Context context) { // 创建显式服务 Intent。 Intent intent = new Intent(); // 指定 SystemUI 包名与服务类名。 intent.setComponent(new ComponentName("com.android.systemui", // 填入 SystemUIService 的完整类名。 "com.android.systemui.SystemUIService")); // 以 user 0 身份启动服务。 context.startServiceAsUser(intent, UserHandle.OWNER);// 结束 SystemUI 启动方法。}
4.3 HOME Intent 如何变成桌面 Activity
mBooting 进入启动状态后,AMS 发起 HOME Activity 启动。
// 标记系统仍在启动阶段。mBooting = true;// 为当前用户启动 HOME Activity。startHomeActivityLocked(mCurrentUserId, "systemReady");下面这段代码连接了 AMS 与 PMS。getHomeIntent() 加入 CATEGORY_HOME;resolveActivityInfo() 经 AppGlobals.getPackageManager() 查询 PMS,得到 ActivityInfo 后再交给 ActivityStackSupervisor。查询目标来自 PMS 已建立的数据结构。
// 构造用于寻找桌面的 Intent。Intent getHomeIntent() { // 使用顶部 action 和 data 创建 Intent。 Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); // 应用可能预设的顶部组件。 intent.setComponent(mTopComponent); // 非低级工厂测试模式才寻找普通桌面。 if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { // 加入 HOME 类别。 intent.addCategory(Intent.CATEGORY_HOME); // 结束模式判断。 } // 返回 HOME Intent。 return intent;// 结束 Intent 构造方法。}// 为指定用户启动桌面。boolean startHomeActivityLocked(int userId, String reason) { // 低级工厂测试且未指定顶部 action 时不启动桌面。 if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL // 同时检查顶部 action 是否为空。 && mTopAction == null) { // 返回未启动状态。 return false; // 结束工厂测试判断。 } // 取得 HOME Intent。 Intent intent = getHomeIntent(); // 通过包管理接口解析目标 ActivityInfo。 ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId); // 找到目标组件后进入启动分支。 if (aInfo != null) { // 把解析结果写成显式组件。 intent.setComponent(new ComponentName( // 使用目标应用包名与 Activity 类名。 aInfo.applicationInfo.packageName, aInfo.name)); // 复制 ActivityInfo,避免修改 PMS 返回的共享对象。 aInfo = new ActivityInfo(aInfo); // 将 ApplicationInfo 调整到目标用户。 aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId); // 查找目标应用的现有进程记录。 ProcessRecord app = getProcessRecordLocked(aInfo.processName, // 使用目标应用 UID,并允许保留记录。 aInfo.applicationInfo.uid, true); // 未运行或未被 instrumentation 接管时启动桌面。 if (app == null || app.instrumentationClass == null) { // 为启动任务添加新任务栈标志。 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); // 交给栈管理器启动 HOME Activity。 mStackSupervisor.startHomeActivity(intent, aInfo, reason); // 结束 instrumentation 状态判断。 } // 结束目标存在判断。 } // 表示 HOME 启动请求已处理。 return true;// 结束 HOME 启动方法。}五、PMS 是怎么创建出来的
5.1 Settings 建立持久化边界
PMS 的 main() 调用构造函数时,大量准备工作已经在 ServiceManager.addService("package", m) 之前完成。下面的代码展示 Settings 的文件边界:packages.xml 保存包设置,packages-backup.xml 用于备份恢复,packages.list 提供包与 UID 等列表信息;packages-stopped.xml 及备份文件在该版本中用于迁移兼容。

// Settings 构造器接收数据目录与共享锁。Settings(File dataDir, Object lock) { // 保存并发访问使用的锁。 mLock = lock; // 建立 /data/system 对应对象。 mSystemDir = new File(dataDir, "system"); // 创建目录及缺失的父目录。 mSystemDir.mkdirs(); // 指向主要包设置文件。 mSettingsFilename = new File(mSystemDir, "packages.xml"); // 指向包设置备份文件。 mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml"); // 指向包列表文件。 mPackageListFilename = new File(mSystemDir, "packages.list"); // 设置包列表文件的权限与属主属组。 FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID); // 指向迁移所需的 stopped 状态文件。 mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); // 指向 stopped 状态备份文件。 mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");// 结束 Settings 构造器。}5.2 共享 UID 与 PMS 初始化

下面的代码在包扫描前把六组系统共享 UID 写入 Settings。解析带 android:sharedUserId 的包时,PMS 才能将它关联到已有的共享用户。
| sharedUserId | UID |
|---|---|
android.uid.system |
1000 |
android.uid.phone |
1001 |
android.uid.bluetooth |
1002 |
android.uid.log |
1007 |
android.uid.nfc |
1027 |
android.uid.shell |
2000 |
// 注册 system 共享用户。mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, // 系统共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 phone 共享用户。mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, // phone 共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 log 共享用户。mSettings.addSharedUserLPw("android.uid.log", LOG_UID, // log 共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 nfc 共享用户。mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, // nfc 共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 bluetooth 共享用户。mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, // bluetooth 共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 shell 共享用户。mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, // shell 共享用户同时具有 privileged 标志。 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);5.3 dexopt、工作线程与目录
下面的代码中,mLazyDexOpt 决定 dexopt 的 LRU 时间窗:为真时是 30 分钟,否则是 7 天。阈值算完、SystemConfig 读完之后,构造函数才建立 ServiceThread 与 PackageHandler,并把 PackageHandler 交给 Watchdog 监控;紧接着建立 /data/app、/data/app-private 等路径字段。/data/app-private 对应 mDrmAppPrivateInstallDir,不能写成其他字段名。

// 声明 dexopt 使用的分钟阈值。long dexOptLRUThresholdInMinutes;// 懒执行 dexopt 时使用较短时间窗。if (mLazyDexOpt) { // eng 场景取最近 30 分钟。 dexOptLRUThresholdInMinutes = 30;// 普通分支使用七天时间窗。} else { // 将七天换算为分钟。 dexOptLRUThresholdInMinutes = 7 * 24 * 60;// 结束阈值分支。}// 将分钟换算为毫秒并保存。mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;// 取得 /data 目录。File dataDir = Environment.getDataDirectory();// 建立第三方 APK 安装目录字段。mAppInstallDir = new File(dataDir, "app");// 建立前向锁定 APK 目录字段。mDrmAppPrivateInstallDir = new File(dataDir, "app-private");构造函数还会处理 /system/framework 的 dex 优化,并依次扫描 /system/framework、/system/priv-app、/system/app、/vendor/app 与数据分区中的应用目录。不同目录附带的 parse/scan flags 不同,决定系统包、privileged 包、vendor 包及 forward-lock 包的处理方式。

下面这段代码是普通第三方包进入扫描链的入口。mOnlyCore 为真时跳过这两次数据目录扫描;这适用于系统可用包范围已经受加密状态约束的启动场景,并不表示安装目录永久固定。
// 非 core-only 模式才扫描数据分区应用。if (!mOnlyCore) { // 记录 PMS 数据扫描开始事件。 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, // 写入开机以来的运行时间。 SystemClock.uptimeMillis()); // 扫描 /data/app 中的普通第三方包。 scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); // 扫描 /data/app-private 中的前向锁定包。 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, // 保留已知包约束并传入当前时间参数。 scanFlags | SCAN_REQUIRE_KNOWN, 0);// 结束数据目录扫描条件。}六、扫描:scanDirLI 与 scanPackageLI
6.1 scanDirLI 的筛选与失败处理

下面这段代码先读取目录。空目录立即返回;循环中只把 APK 或目录视为候选包,并过滤安装暂存名。普通文件命中 continue 后仍保留在原处,因为它没有进入包解析,也没有构成 INSTALL_FAILED_INVALID_APK。删除逻辑服务于“候选用户包解析后被判定为无效 APK”的情况,目录由 Installer 调用 installd 删除,单文件则调用 File.delete()。
// 扫描给定目录中的候选包。private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) { // 读取目录条目。 final File[] files = dir.listFiles(); // 空目录无需继续处理。 if (ArrayUtils.isEmpty(files)) { // 记录空目录信息。 Log.d(TAG, "No files in app dir " + dir); // 返回调用方。 return; // 结束空目录判断。 } // 遍历目录条目。 for (File file : files) { // APK 或目录且名称不是 stage 时才是候选包。 final boolean isPackage = (isApkFile(file) || file.isDirectory()) // 过滤安装暂存目录名。 && !PackageInstallerService.isStageName(file.getName()); // 普通文件直接跳过。 if (!isPackage) { // 处理下一个目录条目。 continue; // 结束候选包判断。 } // 捕获单个候选包的解析异常。 try { // 要求输入必须符合 APK 包格式。 scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK, // 传入扫描标志与时间。 scanFlags, currentTime, null); // 接收包管理异常。 } catch (PackageManagerException e) { // 非系统无效 APK 才进入删除分支。 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 && // 同时检查 INVALID_APK 错误码。 e.error == PackageManager.INSTALL_FAILED_INVALID_APK) { // 目录包交给 Installer 删除。 if (file.isDirectory()) { // 通过 installd 删除包目录。 mInstaller.rmPackageDir(file.getAbsolutePath()); // 单文件走 File API。 } else { // 删除解析失败的 APK 文件。 file.delete(); // 结束文件类型判断。 } // 结束无效用户包判断。 } // 结束异常处理。 } // 结束目录遍历。 }// 结束目录扫描方法。}6.2 scanPackageLI 进入解析器
下面这段代码为每次扫描新建 PackageParser,注入进程拆分、core-only 与显示参数。parsePackage() 的输出是 PackageParser.Package;异常被转换为 PMS 使用的 PackageManagerException。后续签名校验、Settings 关联和扫描结果登记不在本段摘录内。
// 扫描单个包文件或目录。private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, // 接收扫描时间和目标用户。 long currentTime, UserHandle user) throws PackageManagerException { // 合并 PMS 默认解析标志。 parseFlags |= mDefParseFlags; // 为本次包扫描创建解析器。 PackageParser pp = new PackageParser(); // 传入独立进程配置。 pp.setSeparateProcesses(mSeparateProcesses); // 传入 core-only 限制。 pp.setOnlyCoreApps(mOnlyCore); // 传入显示参数供资源解析使用。 pp.setDisplayMetrics(mMetrics); // 声明解析结果对象。 final PackageParser.Package pkg; // 捕获底层解析异常。 try { // 解析候选包并生成 Package。 pkg = pp.parsePackage(scanFile, parseFlags); // 接收 PackageParser 异常。 } catch (PackageParserException e) { // 转换为 PMS 统一异常类型。 throw PackageManagerException.from(e); // 结束异常处理。 } // 省略:签名校验、Settings 查找与扫描结果登记。七、解析 AndroidManifest.xml
7.1 单 APK 与拆分包
/data/app 中既可能出现单个 APK,也可能出现装有 base.apk 和 split APK 的目录。下面的代码根据 File.isDirectory() 分流,目录进入 parseClusterPackage(),单文件进入 parseMonolithicPackage()。

// 根据输入形态选择包解析路径。public Package parsePackage(File packageFile, int flags) throws PackageParserException { // 目录表示 cluster 包布局。 if (packageFile.isDirectory()) { // 解析 base 与 split 组成的包目录。 return parseClusterPackage(packageFile, flags); // 单文件走 monolithic 路径。 } else { // 解析单 APK 文件。 return parseMonolithicPackage(packageFile, flags); // 结束输入形态判断。 }// 结束包解析入口。}7.2 APK 如何进入 AssetManager
下面这段代码先检查 PARSE_MUST_BE_APK,再调用 AssetManager.addAssetPath()。返回的 cookie 标识 AssetManager 中的资源路径,后续打开清单 XML 时仍要使用它。cookie == 0 表示资源路径加载失败。
// 将 APK 资源路径加入 AssetManager。private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) // 声明可能抛出的解析异常。 throws PackageParserException { // 强制 APK 模式下校验路径后缀。 if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { // 抛出非 APK 错误。 throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, // 附带无效路径。 "Invalid package file: " + apkPath); // 结束路径校验。 } // 把 APK 加入资源路径并取得 cookie。 int cookie = assets.addAssetPath(apkPath); // cookie 为零表示加入失败。 if (cookie == 0) { // 抛出清单加载错误。 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, // 附带加载失败路径。 "Failed adding asset path: " + apkPath); // 结束 cookie 校验。 } // 返回资源路径 cookie。 return cookie;// 结束资源加载方法。}
下面这段代码创建 Resources,设置资源配置,再通过 openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME) 打开 APK 内的 AndroidManifest.xml。解析成功后,PMS 为 Package 补充卷标识和基础代码路径;finally 关闭 parser。
// 解析 base APK 文件。private Package parseBaseApk(File apkFile, AssetManager assets, int flags) // 声明解析异常。 throws PackageParserException { // 取得 APK 绝对路径。 final String apkPath = apkFile.getAbsolutePath(); // 声明可扩展存储的卷标识。 String volumeUuid = null; // /mnt/expand/ 前缀表示 APK 位于可扩展存储卷。 if (apkPath.startsWith(MNT_EXPAND)) { // 找卷 UUID 在路径中的结束位置。 final int end = apkPath.indexOf('/', MNT_EXPAND.length()); // 截出这一段卷 UUID。 volumeUuid = apkPath.substring(MNT_EXPAND.length(), end); } // 把 APK 加入 AssetManager。 final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); // 声明资源对象。 Resources res = null; // 声明 XML 解析器。 XmlResourceParser parser = null; // 捕获资源和 XML 解析异常。 try { // 基于 AssetManager 创建 Resources。 res = new Resources(assets, mMetrics, null); // 打开 APK 内的 AndroidManifest.xml。 parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); // 保存解析错误文本。 final String[] outError = new String[1]; // 进入基于 Resources 与 parser 的重载。 final Package pkg = parseBaseApk(res, parser, flags, outError); // 返回空表示清单内容不合法。 if (pkg == null) { // 把错误码与解析位置组成异常信息。 throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); } // 记录 APK 所属的存储卷。 pkg.volumeUuid = volumeUuid; // 写入 APK 的基础代码路径。 pkg.baseCodePath = apkPath; // 签名留给后续的证书校验步骤填写。 pkg.mSignatures = null; // 返回完整度更高的 Package 对象。 return pkg; // 省略:PackageParserException 原样抛出,其余异常包装成 UNEXPECTED_EXCEPTION 再抛。 // 无论成功失败都执行资源清理。 } finally { // 安静地关闭 XML 解析器。 IoUtils.closeQuietly(parser); // 结束资源清理。 }// 结束 base APK 文件解析。}7.3 Manifest 属性节点
下面这段代码进入 XML 内容解析后,先取得 <manifest> 的 package/split 名称。base APK 不接受非空 splitName;随后创建 Package(pkgName),读取 versionCode、revisionCode、versionName 与 sharedUserId。刚执行 new Package(pkgName) 时,其余组件字段还没有填充。
// 解析 base APK 的 manifest 内容。private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, // 接收错误文本数组。 String[] outError) throws XmlPullParserException, IOException { // 将 parser 作为属性集合使用。 AttributeSet attrs = parser; // 声明包名。 final String pkgName; // 声明 split 名称。 final String splitName; // 捕获包名与 split 名称的解析异常。 try { // 调用根标签名称解析。 Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); // 取得 package 名称。 pkgName = packageSplit.first; // 取得 split 名称。 splitName = packageSplit.second; // 名称不合法时记下错误码并放弃这个包。 } catch (PackageParserException e) { // 标记成坏包名错误。 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; // 返回空结果。 return null; // 结束名称解析异常处理。 } // base APK 不允许出现 split 名称。 if (!TextUtils.isEmpty(splitName)) { // 写入可诊断错误。 outError[0] = "Expected base APK, but found split " + splitName; // 返回空结果。 return null; // 结束 split 校验。 } // 用包名创建 Package 模型。 final Package pkg = new Package(pkgName); // 取得 manifest 属性数组。 TypedArray sa = res.obtainAttributes(attrs, // 使用内部 manifest styleable。 com.android.internal.R.styleable.AndroidManifest); // 同时写入 Package 与 ApplicationInfo 的 versionCode。 pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger( // 缺省 versionCode 为零。 com.android.internal.R.styleable.AndroidManifest_versionCode, 0); // 读取 base APK 的 revisionCode。 pkg.baseRevisionCode = sa.getInteger( // 缺省 revisionCode 为零。 com.android.internal.R.styleable.AndroidManifest_revisionCode, 0); // 读取 versionName。 pkg.mVersionName = sa.getNonConfigurationString( // 缺省值参数为零。 com.android.internal.R.styleable.AndroidManifest_versionName, 0); // 读取可选 sharedUserId。 String str = sa.getNonConfigurationString( // 使用 manifest 的 sharedUserId 属性。 com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); // 省略:sharedUserId 的合法性校验分支、installLocation、coreApp, // 以及 application、四大组件与 meta-data 的分发。下面这段代码校验根标签必须为 <manifest>。android 包名跳过常规合法性校验,其他包名调用 validateName();仅当返回错误文本时才抛异常。if (!"android".equals(packageName)) 的含义是“包名不为 android 时执行合法性校验”,不能理解为条件成立就直接抛异常。
// 从 manifest 根标签解析 package 与 split 名称。private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser, // 接收属性与解析标志。 AttributeSet attrs, int flags) throws IOException, XmlPullParserException, // 声明包解析异常。 PackageParserException { // 根标签必须名为 manifest。 if (!parser.getName().equals("manifest")) { // 根标签错误时抛出 malformed 异常。 throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, // 写明缺少 manifest 标签。 "No <manifest> tag"); // 结束根标签校验。 } // 读取 package 属性。 final String packageName = attrs.getAttributeValue(null, "package"); // android 以外的包名才执行常规合法性校验。 if (!"android".equals(packageName)) { // 校验 Java 风格包名。 final String error = validateName(packageName, true, true); // 有错误文本时拒绝该包名。 if (error != null) { // 抛出坏包名异常。 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, // 附带校验错误文本。 "Invalid manifest package: " + error); // 结束错误判断。 } // 结束特殊包名判断。 } // 读取可选 split 属性。 String splitName = attrs.getAttributeValue(null, "split"); // 返回驻留字符串组成的名称对。 return Pair.create(packageName.intern(), // split 为空时原样返回空值。 (splitName != null) ? splitName.intern() : splitName);// 结束名称解析。}7.4 application 与组件公共字段
下面这段代码将 <application android:name> 交给 buildClassName(),相对类名会结合包名转换成完整类名,并写入 ApplicationInfo.className。应用进程创建 Application 时,ActivityThread 使用该信息寻找具体类。
// 解析 application 标签。private boolean parseBaseApplication(Package owner, Resources res, // 接收 XML、属性、标志和错误数组。 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) // 声明 XML 相关异常。 throws XmlPullParserException, IOException { // 取得 Package 持有的 ApplicationInfo。 final ApplicationInfo ai = owner.applicationInfo; // 保存应用包名。 final String pkgName = owner.applicationInfo.packageName; // 取得 application 属性数组。 TypedArray sa = res.obtainAttributes(attrs, // 使用内部 application styleable。 com.android.internal.R.styleable.AndroidManifestApplication); // 读取 android:name。 String name = sa.getNonConfigurationString( // 指定 Application_name 属性。 com.android.internal.R.styleable.AndroidManifestApplication_name, 0); // 声明了类名才做拼接。 if (name != null) { // 转成完整 Application 类名。 ai.className = buildClassName(pkgName, name, outError); // 结束类名处理。 } // 省略:类名失败处理、其他属性与四大组件入口。parseBaseApplication() 遍历 <application> 的子标签时按标签名分发,四个分支分别落到 parseActivity()、parseService()、parseProvider(),解析结果收进 Package 的四个列表。下面这段接在上一段代码之后。
// 读取当前子标签名,后续按名称分发。String tagName = parser.getName();// <activity> 走 parseActivity 的 activity 分支。if (tagName.equals("activity")) { // 最后一个参数继承 application 层的硬件加速设置。 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false, owner.baseHardwareAccelerated); // 单个组件解析失败就终止整个包的解析。 if (a == null) { // 标记清单格式错误。 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; // 返回失败。 return false; } // 收进 Package 的 activities 列表。 owner.activities.add(a);// <receiver> 复用 parseActivity,只是 receiver 参数为 true。} else if (tagName.equals("receiver")) { // 以 receiver 模式解析。 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false); // 单个组件解析失败就终止整个包的解析。 if (a == null) { // 标记清单格式错误。 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; // 返回失败。 return false; } // 收进 Package 的 receivers 列表。 owner.receivers.add(a);// <service> 走 parseService。} else if (tagName.equals("service")) { // 解析 service 标签。 Service s = parseService(owner, res, parser, attrs, flags, outError); // 单个组件解析失败就终止整个包的解析。 if (s == null) { // 标记清单格式错误。 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; // 返回失败。 return false; } // 收进 Package 的 services 列表。 owner.services.add(s);// <provider> 走 parseProvider。} else if (tagName.equals("provider")) { // 解析 provider 标签。 Provider p = parseProvider(owner, res, parser, attrs, flags, outError); // 单个组件解析失败就终止整个包的解析。 if (p == null) { // 标记清单格式错误。 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; // 返回失败。 return false; } // 收进 Package 的 providers 列表。 owner.providers.add(p);// 其余标签走后面的分支。} else if (tagName.equals("activity-alias")) { // 省略:activity-alias、library、uses-library 与 meta-data 的分支。}
下面这段代码是不同组件共用的名称、图标和标签解析逻辑。组件必须声明 android:name,.MainActivity 这类相对名称在此补全;PackageItemInfo.packageName 最终指回所属包。
// 解析组件共有的 PackageItemInfo 字段。private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, // 接收错误、标签、属性和资源索引。 String[] outError, String tag, TypedArray sa, // 接收名称、标签、图标、徽标与横幅索引。 int nameRes, int labelRes, int iconRes, int logoRes, int bannerRes) { // 读取组件 android:name。 String name = sa.getNonConfigurationString(nameRes, 0); // 组件名缺失时解析失败。 if (name == null) { // 写入缺少名称的错误。 outError[0] = tag + " does not specify android:name"; // 返回失败。 return false; // 结束名称缺失判断。 } // 将相对类名补全为完整名称。 outInfo.name = buildClassName(owner.applicationInfo.packageName, name, outError); // 补全失败同样判定为解析失败。 if (outInfo.name == null) { // 返回失败。 return false; // 结束类名补全判断。 } // 读取组件图标资源 id。 int iconVal = sa.getResourceId(iconRes, 0); // 声明了图标就记下来。 if (iconVal != 0) { // 写入图标资源 id。 outInfo.icon = iconVal; // 图标优先,清掉可能存在的字面标签。 outInfo.nonLocalizedLabel = null; } // 读取组件 logo 资源 id。 int logoVal = sa.getResourceId(logoRes, 0); // 声明了 logo 就记下来。 if (logoVal != 0) { // 写入 logo 资源 id。 outInfo.logo = logoVal; } // 读取组件 banner 资源 id。 int bannerVal = sa.getResourceId(bannerRes, 0); // 声明了 banner 就记下来。 if (bannerVal != 0) { // 写入 banner 资源 id。 outInfo.banner = bannerVal; } // 读取标签,它可能是字面字符串也可能是资源引用。 TypedValue v = sa.peekValue(labelRes); // 标签是字面字符串时转成非本地化标签。 if (v != null && (outInfo.labelRes=v.resourceId) == 0) { // 记录字面标签文本。 outInfo.nonLocalizedLabel = v.coerceToString(); } // 写入组件所属包名。 outInfo.packageName = owner.packageName; // 返回公共字段解析结果。 return true;// 结束公共字段解析方法。}7.5 Activity、launchMode 与 taskAffinity
下面这段代码中的 Activity 是 PackageParser.Activity 静态内部模型,不是运行时的 android.app.Activity 实例。它封装 ActivityInfo 和解析出的 intent 列表。receiver 共用部分解析代码,但其 launchMode 固定为 LAUNCH_MULTIPLE。
// 解析 activity 或 receiver 标签。private Activity parseActivity(Package owner, Resources res, // 接收 XML、属性、标志和错误数组。 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError, // 接收组件类型与硬件加速缺省值。 boolean receiver, boolean hardwareAccelerated) // 声明 XML 解析异常。 throws XmlPullParserException, IOException { // 取得 activity 属性数组。 TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity); // 首次解析组件时创建公共参数对象。 if (mParseActivityArgs == null) { // 传入包、错误数组与各公共属性索引。 mParseActivityArgs = new ParseComponentArgs(owner, outError, // 传入组件名称属性索引。 R.styleable.AndroidManifestActivity_name, // 传入组件标签属性索引。 R.styleable.AndroidManifestActivity_label, // 传入组件图标属性索引。 R.styleable.AndroidManifestActivity_icon, // 传入组件徽标属性索引。 R.styleable.AndroidManifestActivity_logo, // 传入组件横幅属性索引。 R.styleable.AndroidManifestActivity_banner, // 传入独立进程配置。 mSeparateProcesses, // 传入组件进程属性索引。 R.styleable.AndroidManifestActivity_process, // 传入组件描述属性索引。 R.styleable.AndroidManifestActivity_description, // 传入组件启用状态属性索引。 R.styleable.AndroidManifestActivity_enabled); // 结束公共参数初始化。 } // 根据解析对象类型设置标签名。 mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>"; // 将本次属性数组写入公共参数。 mParseActivityArgs.sa = sa; // 将解析标志写入公共参数。 mParseActivityArgs.flags = flags; // 创建清单层的 Activity 模型。 Activity a = new Activity(mParseActivityArgs, new ActivityInfo()); // 公共字段解析失败时停止处理。 if (outError[0] != null) { // 回收 TypedArray。 sa.recycle(); // 返回空结果。 return null; // 结束公共字段错误判断。 } // activity 分支读取 launchMode。 if (!receiver) { // 缺省值对应 standard 模式。 a.info.launchMode = sa.getInt( // 读取 AndroidManifestActivity_launchMode。 R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE); // receiver 不具备 Activity 启动模式。 } else { // 将 receiver 的字段固定为 LAUNCH_MULTIPLE。 a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; // 结束组件类型分支。 } // 省略:其余 Activity 属性与子标签解析。在 Android 6.0.0_r1 中,ActivityInfo 里定义的是 LAUNCH_MULTIPLE=0、LAUNCH_SINGLE_TOP=1、LAUNCH_SINGLE_TASK=2、LAUNCH_SINGLE_INSTANCE=3。下面这段代码展示 Activity 的 taskAffinity 继承规则:Activity 没写该属性时,buildTaskAffinityName() 接收 Application 层的默认值;Application 也没有单独指定时,其默认值与包名相关。
// 读取 Activity 的 taskAffinity 属性。str = sa.getNonConfigurationString( // 使用对应 styleable 索引。 R.styleable.AndroidManifestActivity_taskAffinity, // 使用 Android 6.0 源码中的配置版本参数。 Configuration.NATIVE_CONFIG_VERSION);// 结合包名、Application 默认值和组件值生成最终 affinity。a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, // 将 Application 默认值、Activity 声明值与错误数组传入。 owner.applicationInfo.taskAffinity, str, outError);
下面这段代码先处理 <intent-filter> 标签本身:新建 ActivityIntentInfo,再把它交给 parseIntent() 填充。过滤结果只有在至少有一个 action 时才进入 Activity 的 intents 列表,action 为空的 filter 只打一条警告。同一段分支还顺带处理了 <preferred> 与 <meta-data>。
// intent-filter 子标签进入过滤器解析分支。if (parser.getName().equals("intent-filter")) { // 为当前组件新建一个过滤器模型。 ActivityIntentInfo intent = new ActivityIntentInfo(a); // 填充 action、category 与 data。 if (!parseIntent(res, parser, attrs, true, true, intent, outError)) { // 过滤器解析失败,整个 activity 作废。 return null; } // 没有任何 action 的过滤器只记警告。 if (intent.countActions() == 0) { // 输出警告并附带源码位置。 Slog.w(TAG, "No actions in intent filter at " + mArchiveSourcePath + " " + parser.getPositionDescription()); // 有效的过滤器才登记。 } else { // 收进 Activity 的 intents 列表。 a.intents.add(intent); }// receiver 之外还允许 preferred 过滤器。} else if (!receiver && parser.getName().equals("preferred")) { // 省略:preferred 过滤器的处理分支。// meta-data 单独走自己的解析方法。} else if (parser.getName().equals("meta-data")) { // 解析 meta-data 并挂到组件上。 if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData, outError)) == null) { // meta-data 解析失败同样作废。 return null; }}过滤器内部的 action 与 category 由 parseIntent() 逐个读取,字符串分别进入 ActivityIntentInfo 的两个集合。
// 读取 intent-filter 的当前子标签名。String nodeName = parser.getName();// action 标签进入动作分支。if (nodeName.equals("action")) { // 读取 android:name 属性。 String value = attrs.getAttributeValue(ANDROID_RESOURCES, "name"); // 拒绝缺少名称或名称为空的 action。 if (value == null || value == "") { // 写入缺少 android:name 的错误。 outError[0] = "No value supplied for <android:name>"; // 返回解析失败。 return false; // 结束 action 名称校验。 } // 跳过当前 XML 标签余下内容。 XmlUtils.skipCurrentTag(parser); // 将动作字符串加入 filter。 outInfo.addAction(value);// category 标签进入类别分支。} else if (nodeName.equals("category")) { // 读取 android:name 属性。 String value = attrs.getAttributeValue(ANDROID_RESOURCES, "name"); // 拒绝缺少名称或名称为空的 category。 if (value == null || value == "") { // 写入缺少 android:name 的错误。 outError[0] = "No value supplied for <android:name>"; // 返回解析失败。 return false; // 结束 category 名称校验。 } // 跳过当前 XML 标签余下内容。 XmlUtils.skipCurrentTag(parser); // 将类别字符串加入 filter。 outInfo.addCategory(value);// data 标签进入数据匹配分支。} else if (nodeName.equals("data")) { // 省略:mimeType、scheme、host 与 port 的原始解析分支。7.6 manifest示例解析
PMS 读到 package="com.example.myapplication" 后创建 Package;android:name="BaseApplication" 进入 application 类名解析;.MainActivity 经公共组件逻辑补全类名,singleTask 写入 ActivityInfo.launchMode,MAIN/LAUNCHER 则进入 ActivityIntentInfo。
<!-- 声明 XML 版本与字符编码。 --><?xml version="1.0" encoding="utf-8"?><!-- 声明 Android 命名空间与应用包名。 --><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <!-- 声明读取外部存储权限。 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 声明 Application 及素材中出现的基础属性。 --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:name="BaseApplication" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <!-- 声明使用相对类名且启动模式为 singleTask 的主 Activity。 --> <activity android:name=".MainActivity" android:launchMode="singleTask"> <!-- 声明入口 Intent 过滤器。 --> <intent-filter> <!-- 声明 MAIN 动作。 --> <action android:name="android.intent.action.MAIN" /> <!-- 声明 LAUNCHER 类别。 --> <category android:name="android.intent.category.LAUNCHER" /> <!-- 结束入口过滤器。 --> </intent-filter> <!-- 结束 Activity 声明。 --> </activity> <!-- 结束 Application 声明。 --> </application><!-- 结束 manifest。 --></manifest>示例中的 BaseApplication 只覆盖 onCreate() 与 attachBaseContext()。
// 声明示例包名。package com.example.myapplication;// 导入 Application 基类。import android.app.Application;// 导入 Context 类型。import android.content.Context;// 定义自定义 Application。public class BaseApplication extends Application { // 覆盖 Application.onCreate。 @Override // 接收应用创建回调。 public void onCreate() { // 调用父类实现。 super.onCreate(); // 结束创建回调。 } // 覆盖基础 Context 附加方法。 @Override // 接收基础 Context。 protected void attachBaseContext(Context base) { // 调用父类实现。 super.attachBaseContext(base); // 结束 Context 附加方法。 }// 结束自定义 Application。}八、为什么需要缓存:AMS 与 PMS 的分工

整个过程可以归纳为一条跨服务链:PMS 在扫描期把清单转成 Package 及其中的 ApplicationInfo、Activity、Service、Receiver、Provider、permission 和 intent 信息;AMS 处理 Activity 启动时,经包管理 Binder 接口查询具体 ActivityInfo。Binder 是这里的进程通信接口边界,不过 Android 6.0 的 AMS 与 PMS 同处 system_server;PMS 还通过 Installer 与 installd 处理 dexopt、目录创建或删除等文件系统操作。
// 启动 Installer 并取得实例。Installer installer = mSystemServiceManager.startService(Installer.class);// 通过 Lifecycle 创建并取得 AMS 本体。mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();// 将 Installer 依赖交给 AMS。mActivityManagerService.setInstaller(installer);// AMS ready 后发起桌面启动。startHomeActivityLocked(mCurrentUserId, "systemReady");// SystemServer 的 ready 回调启动 SystemUI。startSystemUi(context);// PMS 扫描普通第三方包安装目录。scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);// 单包扫描进入 PackageParser。pkg = pp.parsePackage(scanFile, parseFlags);上段代码分散在不同方法中,用于对应素材总结中的节点,不表示它们在同一个方法内连续执行。HOME 路径可直接观察同一分工:resolveActivityInfo() 查询 PMS,mStackSupervisor.startHomeActivity() 使用返回的 ActivityInfo。
内存缓存让 startActivity 避开重复的 APK/XML 解析。Settings 管理的 packages.xml 等文件形成持久化边界,用于恢复已知包设置并辅助后续扫描;“下次开机完全不解析所有 APK”会超过本文代码片段能支持的范围。准确的说法是 PMS 会读取已有设置,并结合目录内容、文件状态与扫描策略更新内存包状态。
九、小结与动手验证
一个已安装 APK 从 /data/app 进入可启动状态,负责加工清单数据的是 PMS,时机主要位于 system_server 的引导服务阶段。PackageManagerService 构造期间扫描应用目录,scanDirLI() 过滤候选项,scanPackageLI() 创建 PackageParser;解析器把 AndroidManifest.xml 的 application、组件与 intent-filter 写入 Package 模型。AMS 在运行期接收启动请求并查询 PMS 返回的 ActivityInfo,随后由 Activity 栈管理链执行启动。
AMS 与 PMS 均由 startBootstrapServices() 创建。AMS 的 systemReady() 位于 startOtherServices() 尾部,负责发起 HOME Activity 启动;其回调推进 boot phase 并启动 SystemUI。这解释了包信息准备和界面拉起为何处在不同阶段。
可在具备相应权限的 Android 6.0 测试设备或模拟器上检查包管理状态:
adb shell dumpsys package com.example.myapplicationadb shell su -c 'cat /data/system/packages.xml'adb logcat -b events | grep BOOT_PROGRESS_PMSdumpsys package 用于观察 PMS 已缓存的包与组件信息;读取 /data/system/packages.xml 通常需要 root 或调试系统权限;events 日志中的 BOOT_PROGRESS_PMS_* 可定位 PMS 扫描阶段。
本文由 AgentWorkFlow 生成