Skip to content

dexopt 编译模式

分析 Android 17 dexopt reason、compiler filter、profile flags 和最终模式调整。

AndroidPMSDexOptART

dexopt 编译模式 ​

dexopt 编译模式不是 PMS 中一个固定的 speed/verify 枚举。Android 17 将“为什么编译”表示为 reason,将“想要什么编译结果”表示为 compiler filter,再通过安装条件和 ART Service 的策略调整最终执行模式。

1. 三个输入 ​

输入来源作用
reasonInstallScenarioHelper / boot path选择默认策略和优先级
compiler filtersession 参数或 ART 配置目标编译深度
flagsinstall flags / ART flags忽略 profile、primary/secondary 等

2. reason 映射 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java

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

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

java
case "--dexopt-compiler-filter":
    sessionParams.dexoptCompilerFilter = getNextArgRequired();
    break;

PackageInstaller session 可以显式传入安装期间的 compiler filter。帮助文本同时说明 --ignore-dexopt-profile 只影响本次安装,不影响之后的后台 dexopt 和手动 compile 命令。

java
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

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。

java
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() 中。

java
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 失败而自动失败。

java
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. 排查清单 ​

  1. 先看 requested reason 和 compiler filter,再看实际 filter。
  2. 检查显式 compiler filter 是否覆盖了 NOOP skip 条件。
  3. 区分 shouldCallArtService=false 和 ART 收到 NOOP 参数。
  4. profile 相关问题检查 install flag,而不是只看应用是否 debuggable。
  5. 结果异常检查每个 dex container 的 status、ABI、耗时和 actual filter。
  6. 若请求 filter 合法但实际被降级,继续到 ART Service Dexopter 查看上下文/安全模式/profile 条件。

11. 源码阅读路线 ​

  1. DexOptHelper.getDexoptParamsByInstallRequest:PMS 如何组装请求。
  2. InstallScenarioHelper / ART ReasonMapping:reason 到默认策略。
  3. shouldCallArtService/shouldSkipDexopt:调用和 NOOP 的边界。
  4. ArtManagerLocal.dexoptPackage:PMS/ART 的交接点。
  5. ART Service 的 Dexopter:实际 compiler filter 调整和 dex2oat 参数。
  6. InstallRequest.onDexoptFinished:PMS 对结果的消费。

编译模式的关键不是记住 speed 或 verify 的静态定义,而是理解 requested filter、reason、install flags、包状态和 ART 最终调整之间的覆盖关系。Android 17 中,PMS 决定“请求什么”,ART Service 决定“实际编什么”。