dexopt 编译模式
dexopt 编译模式不是 PMS 中一个固定的 speed/verify 枚举。Android 17 将“为什么编译”表示为 reason,将“想要什么编译结果”表示为 compiler filter,再通过安装条件和 ART Service 的策略调整最终执行模式。
1. 三个输入
| 输入 | 来源 | 作用 |
|---|---|---|
| reason | InstallScenarioHelper / boot path | 选择默认策略和优先级 |
| compiler filter | session 参数或 ART 配置 | 目标编译深度 |
| flags | install flags / ART flags | 忽略 profile、primary/secondary 等 |
2. reason 映射
源码文件: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 不在这里直接写死 speed-profile。reason 由安装场景 helper 提供,显式 session compiler filter 覆盖默认值;debuggable/Incremental FS 只把 filter 设为 NOOP;ignore profile 则通过 flags 传给 ART。
3. 安装场景
源码文件:frameworks/base/core/java/android/content/pm/PackageManager.java
public static final int INSTALL_SCENARIO_DEFAULT = 0;
public static final int INSTALL_SCENARIO_FAST = 1;
public static final int INSTALL_SCENARIO_BULK = 2;
public static final int INSTALL_SCENARIO_BULK_SECONDARY = 3;场景由 PackageInstaller/安装器传入。DEFAULT 面向普通单包安装,FAST 优先安装完成,BULK 用于批量恢复,SECONDARY 是批量中的低优先级包。具体 reason 与设备电量/热状态的映射位于安装场景 helper 和 ART Service reason mapping,不应从单个 compiler filter 反推完整策略。
4. filter 来源
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
case "--dexopt-compiler-filter":
sessionParams.dexoptCompilerFilter = getNextArgRequired();
break;PackageInstaller session 可以显式传入安装期间的 compiler filter。帮助文本同时说明 --ignore-dexopt-profile 只影响本次安装,不影响之后的后台 dexopt 和手动 compile 命令。
case "--ignore-dexopt-profile":
sessionParams.installFlags |=
PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
break;因此命令行 filter 是 request 级输入,profile ignore 是 install flag;两者最终在 DexoptParams 中组合。
5. 跳过和 NOOP
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
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 filter,而不是让上层完全丢弃请求。这样 ART Service 仍可以返回统一的 DexoptResult,PMS 也能记录安装 dexopt 状态。
6. filter 覆盖
getDexoptParamsByInstallRequest 的覆盖顺序是:先由 reason 建立 Builder,再由 restore/setup 设置 priority,显式 compiler filter 覆盖默认 filter,skip 条件只在没有显式 filter 时设置 NOOP,最后设置 ignore-profile flags。理解顺序很重要:显式 --dexopt-compiler-filter 可能覆盖 skip 条件生成的 NOOP。
if (installRequest.getDexoptCompilerFilter() != null) {
builder.setCompilerFilter(
installRequest.getDexoptCompilerFilter());
} else if (shouldSkipDexopt(installRequest)) {
builder.setCompilerFilter(
DexoptParams.COMPILER_FILTER_NOOP);
}7. 运行时最终调整
Android 17 的最终 filter 调整位于 ART Service 的 Dexopter,而不是 PMS。PMS 传入 requested filter,ART 根据包状态、类加载器上下文、profile、debuggable/vmSafeMode、嵌入式 dex 等条件决定实际 filter。PMS 可观察的结果在 DexoptResult.DexContainerFileDexoptResult.getActualCompilerFilter() 中。
for (PackageDexoptResult packageResult :
dexoptResult.getPackageDexoptResults()) {
for (DexContainerFileDexoptResult fileResult :
packageResult.getDexContainerFileDexoptResults()) {
String actualFilter =
fileResult.getActualCompilerFilter();
int status = fileResult.getStatus();
}
}请求 filter 与实际 filter 可能不同;例如安全模式、缺失 class loader context 或 ignore profile 会导致 ART 降级。调试时不能只看 session 参数。
8. 结果状态
DexoptResult 对每个 dex container 保存状态、实际 filter、ABI、耗时和产物大小。整体状态由 ART Service 汇总,PMS 只记录最终状态和 warning;安装不因单个 dex container 失败而自动失败。
public void onDexoptFinished(
DexoptResult dexoptResult) {
mDexoptStatus = dexoptResult.getFinalStatus();
for (PackageDexoptResult result :
dexoptResult.getPackageDexoptResults()) {
for (DexContainerFileDexoptResult file :
result.getDexContainerFileDexoptResults()) {
if (file.getStatus()
== DexoptResult.DEXOPT_FAILED) {
addWarning("Error occurred during dexopt");
}
}
}
}9. 调试与 profile
debuggable 包默认进入 NOOP 选择,原因是调试需要保持解释/JIT 路径和可预测的调试行为;INSTALL_IGNORE_DEXOPT_PROFILE 则只在当前安装请求中忽略 profile。后续后台 dexopt 仍可以重新使用 profile,不能把安装时 mode 当作永久包属性。
10. 排查清单
- 先看 requested reason 和 compiler filter,再看实际 filter。
- 检查显式 compiler filter 是否覆盖了 NOOP skip 条件。
- 区分
shouldCallArtService=false和 ART 收到 NOOP 参数。 - profile 相关问题检查 install flag,而不是只看应用是否 debuggable。
- 结果异常检查每个 dex container 的 status、ABI、耗时和 actual filter。
- 若请求 filter 合法但实际被降级,继续到 ART Service Dexopter 查看上下文/安全模式/profile 条件。
11. 源码阅读路线
DexOptHelper.getDexoptParamsByInstallRequest:PMS 如何组装请求。InstallScenarioHelper/ ARTReasonMapping:reason 到默认策略。shouldCallArtService/shouldSkipDexopt:调用和 NOOP 的边界。ArtManagerLocal.dexoptPackage:PMS/ART 的交接点。- ART Service 的 Dexopter:实际 compiler filter 调整和 dex2oat 参数。
InstallRequest.onDexoptFinished:PMS 对结果的消费。
编译模式的关键不是记住 speed 或 verify 的静态定义,而是理解 requested filter、reason、install flags、包状态和 ART 最终调整之间的覆盖关系。Android 17 中,PMS 决定“请求什么”,ART Service 决定“实际编什么”。
