PackageDexOptimizer 逻辑
计划中的 PackageDexOptimizer 是旧架构名称。Android 17 的 PMS 源码中,安装 dexopt 的执行入口已经是 DexOptHelper,实际编译由 ART Service 的 ArtManagerLocal 完成;PMS 不再自己组装 dex2oat 命令,也没有一个可直接调用的 PackageDexOptimizer 类。
1. 架构迁移
| 历史职责 | Android 17 owner |
|---|---|
| 选择安装编译 reason | DexOptHelper / InstallScenarioHelper |
| 组装 dexopt 参数 | DexOptHelper.getDexoptParamsByInstallRequest |
| 遍历 dex 容器并编译 | ART Service |
| 生成 oat/vdex/art | ART Service / artd |
| 返回每文件结果 | DexoptResult |
| 安装请求记录状态 | InstallRequest.onDexoptFinished |
2. 安装入口
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
CompletableFuture<Void> dexoptFuture =
mPm.getDexOptHelper()
.performDexoptIfNeededAsync(request);安装包扫描完成后,InstallPackageHelper 把 InstallRequest 交给 DexOptHelper。返回 Future 是安装流程的同步点:是否等待它由调用方的安装编排决定,但 dexopt 本身在专用 executor 中执行。
3. 参数组装
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
private DexoptParams getDexoptParamsByInstallRequest(
InstallRequest installRequest) {
String compilationReason = mInstallScenarioHelper
.getCompilationReasonForInstallScenario(
installRequest.getInstallScenario());
var builder = new DexoptParams.Builder(compilationReason);
if (installRequest.getInstallReason()
== INSTALL_REASON_DEVICE_RESTORE
|| installRequest.getInstallReason()
== INSTALL_REASON_DEVICE_SETUP) {
builder.setPriorityClass(
ArtFlags.PRIORITY_INTERACTIVE_FAST);
}
if (installRequest.getDexoptCompilerFilter() != null) {
builder.setCompilerFilter(
installRequest.getDexoptCompilerFilter());
} else if (shouldSkipDexopt(installRequest)) {
builder.setCompilerFilter(
DexoptParams.COMPILER_FILTER_NOOP);
}
if ((installRequest.getInstallFlags()
& PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE) != 0) {
builder.setFlags(
ArtFlags.FLAG_IGNORE_PROFILE,
ArtFlags.FLAG_IGNORE_PROFILE);
}
return builder.build();
}PMS 只组装 reason、priority、compiler filter 和 profile flag。真正的 filter 调整、dex 文件遍历、ABI 选择和产物写入属于 ART Service。
4. 安装场景 reason
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallScenarioHelper.java
public String getCompilationReasonForInstallScenario(
int installScenario) {
switch (installScenario) {
case PackageManager.INSTALL_SCENARIO_DEFAULT:
return ReasonMapping.REASON_INSTALL;
case PackageManager.INSTALL_SCENARIO_FAST:
return ReasonMapping.REASON_INSTALL_FAST;
case PackageManager.INSTALL_SCENARIO_BULK:
return shouldDowngradeBulkInstall()
? ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED
: ReasonMapping.REASON_INSTALL_BULK;
case PackageManager.INSTALL_SCENARIO_BULK_SECONDARY:
return shouldDowngradeBulkInstall()
? ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED
: ReasonMapping.REASON_INSTALL_BULK_SECONDARY;
default:
return ReasonMapping.REASON_INSTALL;
}
}reason 不是日志文本,而是 ART Service 的策略输入。bulk 场景还会根据电量/热状态选择 downgraded reason,影响 ART 后续编译强度。
5. ART 调用条件
private static boolean shouldCallArtService(
InstallRequest installRequest) {
final boolean isApex =
(installRequest.getScanFlags() & SCAN_AS_APEX) != 0;
final boolean instantApp =
(installRequest.getScanFlags()
& SCAN_AS_INSTANT_APP) != 0;
final PackageSetting ps =
installRequest.getScannedPackageSetting();
final AndroidPackage pkg = ps.getPkg();
final boolean performDexOptForRollback =
!(installRequest.isRollback()
&& installRequest.getInstallSource()
.mInitiatingPackageName.equals("android"));
return !instantApp && pkg != null
&& !isApex && performDexOptForRollback;
}APEX、Instant App、没有已扫描 AndroidPackage、system 发起 rollback 不调用 ART Service。这个判断发生在异步任务提交前,返回 completed Future。
6. NOOP 边界
private static boolean shouldSkipDexopt(
InstallRequest installRequest) {
PackageSetting ps =
installRequest.getScannedPackageSetting();
AndroidPackage pkg = ps.getPkg();
boolean onIncremental =
isIncrementalPath(ps.getPathString());
return pkg == null || pkg.isDebuggable() || onIncremental;
}debuggable 或 Incremental FS 只触发 NOOP compiler filter,不等于完全跳过 ART Service。这样 ART 仍能接收统一请求、记录结果,并由自身决定是否有可执行的优化动作。
7. Snapshot 交接
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
private DexoptResult dexoptPackageUsingArtService(
InstallRequest installRequest) {
final PackageSetting ps =
installRequest.getScannedPackageSetting();
PackageManagerLocal packageManagerLocal =
LocalManagerRegistry.getManager(
PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
PackageManagerLocalImpl.withFilteredSnapshot(
packageManagerLocal, ps)) {
DexoptParams params =
getDexoptParamsByInstallRequest(installRequest);
return getArtManagerLocal().dexoptPackage(
snapshot, ps.getPackageName(), params);
}
}PMS 以目标 PackageSetting 创建 filtered snapshot,再把包名和参数交给 ART。快照是跨模块契约:ART 可以读取编译所需包、代码路径和依赖,但不应直接访问 PMS 的可变全局状态。
8. 异步失败语义
CompletableFuture.runAsync(() -> {
try {
Trace.traceBegin(
TRACE_TAG_PACKAGE_MANAGER, "dexopt");
DexoptResult result =
dexoptPackageUsingArtService(installRequest);
installRequest.onDexoptFinished(result);
} finally {
Trace.traceEnd(
TRACE_TAG_PACKAGE_MANAGER);
}
}, sDexoptExecutor).exceptionally(t -> {
Slog.wtf(TAG,
"Dexopt encountered a fatal error", t);
return null;
});正常编译失败应作为 DexoptResult.DEXOPT_FAILED 返回;只有未预期异常才进入 exceptionally。普通 APK 安装不因 dexopt 失败而失败,运行时可以解释执行,后续后台 dexopt 再尝试。
9. 结果结构
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallRequest.java
public void onDexoptFinished(
DexoptResult dexoptResult) {
for (PackageDexoptResult packageResult :
dexoptResult.getPackageDexoptResults()) {
for (DexContainerFileDexoptResult fileResult :
packageResult.getDexContainerFileDexoptResults()) {
if (fileResult.getStatus()
== DexoptResult.DEXOPT_FAILED) {
addWarning(
"Error occurred during dexopt when "
+ "processing external profiles");
}
}
}
mDexoptStatus = dexoptResult.getFinalStatus();
}InstallRequest 记录最终状态和 warning;它不重新执行编译,也不把单个 dex 容器失败改写为安装失败。调试时应继续查看每个 dex container 的实际 filter 和 status。
10. dump 与人工验证
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
public static void dumpDexoptState(
@NonNull IndentingPrintWriter ipw,
@NonNull PackageDataSnapshot computer,
@Nullable String packageName) {
try (PackageManagerLocal.FilteredSnapshot snapshot =
getPackageManagerLocal()
.withUnownedFilteredSnapshot(computer)) {
if (packageName != null) {
getArtManagerLocal().dumpPackage(
ipw, snapshot, packageName);
} else {
getArtManagerLocal().dump(ipw, snapshot);
}
}
}dumpsys package 的 dexopt 状态由 ART manager 输出。单包 dump 遇到并发删除可能抛 IllegalArgumentException;这反映的是快照与当前包集合的竞态,不代表编译器一定失败。
11. 排查清单
- 先确认安装请求是否真正进入
performDexoptIfNeededAsync。 shouldCallArtService=false时检查 APEX、Instant App、pkg 为空和 system rollback。- ART 被调用但无产物时检查
NOOP、Incremental FS、compiler filter 和 ART Service 状态。 - 查看
DexoptResult的 package/dex container 明细,不只看安装结果码。 - 检查 filtered snapshot 是否包含目标包和代码路径。
- 用
dumpDexoptState对照实际 reason、filter、status 和 artifacts。
12. 源码阅读路线
InstallPackageHelper:确认安装流程何时提交 dexopt。DexOptHelper.getDexoptParamsByInstallRequest:参数来源和优先级。shouldCallArtService/shouldSkipDexopt:调用与 NOOP 边界。dexoptPackageUsingArtService:filtered snapshot 和 ART 交接。InstallRequest.onDexoptFinished:状态、warning 和安装继续语义。- ART Service 的
ArtManagerLocal.dexoptPackage:继续阅读实际编译执行。
Android 17 的 PackageDexOptimizer 职责已经拆分:PMS 负责调度和上下文,ART Service 负责优化决策与执行,InstallRequest 负责接收结果。理解这个边界,比继续寻找一个不存在的 PMS 内部 PackageDexOptimizer 类更接近当前源码。
