DexOptHelper 入口
DexOptHelper 是 PMS 与 ART Service 之间的编排层。它不实现 dex2oat,也不直接决定每个方法的机器码布局,而是负责建立 ArtManagerLocal、选择启动 reason、把安装请求转换为 DexoptParams、决定是否调用 ART,以及把结果和异常交回安装流程。
1. 类的状态
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
private static final ThreadPoolExecutor sDexoptExecutor =
new ThreadPoolExecutor(
1, 1, 60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
private static boolean sArtManagerLocalIsInitialized = false;
private final PackageManagerService mPm;
private final InstallScenarioHelper mInstallScenarioHelper;
static {
sDexoptExecutor.allowsCoreThreadTimeOut();
}
DexOptHelper(PackageManagerService pm) {
mPm = pm;
mInstallScenarioHelper =
new InstallScenarioHelper(mPm.mContext);
}executor 是单线程、无界队列,安装 dexopt 按提交顺序串行执行;核心线程空闲 60 秒可回收。sArtManagerLocalIsInitialized 只表示 LocalManager 中是否已经注册 ART manager,不表示某个包已经完成编译。
2. ART Manager
public static void initializeArtManagerLocal(
@NonNull Context systemContext,
@NonNull PackageManagerService pm) {
ArtManagerLocal artManager =
new ArtManagerLocal(systemContext);
artManager.addDexoptDoneCallback(
false /* onlyIncludeUpdates */, Runnable::run,
pm.getDexOptHelper().new DexoptDoneHandler());
LocalManagerRegistry.addManager(
ArtManagerLocal.class, artManager);
sArtManagerLocalIsInitialized = true;PMS 创建 ART manager 后立即注册 dexopt 完成回调,再写入 LocalManagerRegistry。回调使用 Runnable::run,由 ART manager 的调用线程直接执行 PMS 的完成处理。
systemContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
context.unregisterReceiver(this);
artManager.scheduleBackgroundDexoptJob();
}
}, new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED));
StagedApexObserver.registerForStagedApexUpdates(artManager);
}后台 dexopt Job 不在初始化时立即调度,而是等 LOCKED_BOOT_COMPLETED 后调度;StagedApexObserver 则从初始化阶段开始监听 staged APEX 更新。初始化顺序错误时,getArtManagerLocal() 会抛 RuntimeException,这能暴露启动依赖问题。
3. 启动 dexopt
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
public void performPackageDexOptUpgradeIfNeeded() {
PackageManagerServiceUtils.enforceSystemOrRoot(
"Only the system can request package update");
String reason;
if (mPm.isFirstBoot()) {
reason = ReasonMapping.REASON_FIRST_BOOT;
} else if (mPm.isDeviceUpgrading()) {
reason = ReasonMapping.REASON_BOOT_AFTER_OTA;
} else if (hasBcpApexesChanged()) {
reason = ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE;
} else {
return;
}
Log.i(TAG, "Starting boot dexopt for reason " + reason);
getArtManagerLocal().onBoot(reason, null, null);
}只有 system/root 能触发启动级 dexopt。普通重启直接返回;首次启动、OTA 和 BCP APEX 变化分别映射到 ART 的不同 reason,ART Service 再据此决定范围和编译策略。
4. BCP APEX 判断
private static List<String> getBcpApexes() {
String bcp = System.getenv("BOOTCLASSPATH");
if (TextUtils.isEmpty(bcp)) {
Log.e(TAG, "Unable to get BOOTCLASSPATH");
return List.of();
}
ArrayList<String> result = new ArrayList<>();
for (String pathStr : bcp.split(":")) {
Path path = Paths.get(pathStr);
if (path.getNameCount() >= 2
&& path.getName(0).toString().equals("apex")) {
result.add(path.getName(1).toString());
}
}
return result;
}
private static boolean hasBcpApexesChanged() {
Set<String> bcpApexes =
new HashSet<>(getBcpApexes());
for (ActiveApexInfo info :
ApexManager.getInstance().getActiveApexInfos()) {
if (bcpApexes.contains(info.apexModuleName)
&& info.activeApexChanged) {
return true;
}
}
return false;
}检测只关心 BOOTCLASSPATH 中的 APEX module。APEX 更新但不参与 boot classpath 时,不会触发这个启动 reason;路径解析失败返回空列表并记录错误。
5. 安装参数组装
private DexoptParams getDexoptParamsByInstallRequest(
InstallRequest installRequest) {
String reason = mInstallScenarioHelper
.getCompilationReasonForInstallScenario(
installRequest.getInstallScenario());
var builder = new DexoptParams.Builder(reason);
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();
}安装场景映射 compilation reason;设备恢复/设置提高 priority;显式 compiler filter 优先级最高;跳过条件通过 NOOP 交给 ART。INSTALL_IGNORE_DEXOPT_PROFILE 只影响本次安装,不会禁用未来的后台优化。
6. 调用与跳过
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 rollback =
!(installRequest.isRollback()
&& installRequest.getInstallSource()
.mInitiatingPackageName.equals("android"));
return !instantApp && pkg != null && !isApex && rollback;
}
private static boolean shouldSkipDexopt(
InstallRequest installRequest) {
PackageSetting ps =
installRequest.getScannedPackageSetting();
AndroidPackage pkg = ps.getPkg();
boolean incremental =
isIncrementalPath(ps.getPathString());
return pkg == null || pkg.isDebuggable() || incremental;
}shouldCallArtService=false 意味着直接返回 completed Future;shouldSkipDexopt=true 仍会调用 ART Service,但带 COMPILER_FILTER_NOOP。Android 17 明确将这两类条件分开,便于 ART 统一记录和决定后续行为。
7. 安装异步执行
CompletableFuture<Void> performDexoptIfNeededAsync(
InstallRequest installRequest) {
if (!shouldCallArtService(installRequest)) {
return CompletableFuture.completedFuture(null);
}
return 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;
});
}正常 dexopt 失败应表现为 DexoptResult.DEXOPT_FAILED;只有未预期异常才进入 exceptionally。两者都不把普通应用安装改成失败,安装流程可以继续使用解释执行。
8. 完成回调
源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java
private class DexoptDoneHandler
implements ArtManagerLocal.DexoptDoneCallback {
@Override
public void onDexoptDone(@NonNull DexoptResult result) {
var updatedPackages = new ArraySet<String>();
for (DexoptResult.PackageDexoptResult pkgRes :
result.getPackageDexoptResults()) {
if (pkgRes.hasUpdatedArtifacts()) {
updatedPackages.add(pkgRes.getPackageName());
}
}
if (!updatedPackages.isEmpty()) {
LocalServices.getService(PinnerService.class)
.update(updatedPackages, false /* force */);
}
}
}ART Service 报告产生新 artifacts 的包后,DexOptHelper 通知 PinnerService 更新内存锁定集合。dexopt 产物更新和 pinner 更新是两个消费者,前者由 ART 负责,后者由 PMS 回调串联。
9. 状态转储
public static void dumpDexoptState(
@NonNull IndentingPrintWriter ipw,
@NonNull PackageDataSnapshot computer,
@Nullable String packageName) {
try (PackageManagerLocal.FilteredSnapshot snapshot =
getPackageManagerLocal()
.withUnownedFilteredSnapshot(computer)) {
if (packageName != null) {
try {
getArtManagerLocal().dumpPackage(
ipw, snapshot, packageName);
} catch (IllegalArgumentException e) {
ipw.println(e);
}
} else {
getArtManagerLocal().dump(ipw, snapshot);
}
}
}dump 使用 filtered snapshot,单包查询遇到并发删除时把 IllegalArgumentException 打印出来;全量查询直接让 ART manager 输出所有包状态。dump 反映的是 ART 当前状态,不等同于“本次安装是否调用过 dexopt”。
10. 排查顺序
- 启动 dexopt 没发生:检查首次启动、OTA、BCP APEX 变化和 system/root caller。
- 安装未调用 ART:检查 APEX、Instant App、包对象为空和 system rollback。
- ART 被调用但无编译产物:检查
shouldSkipDexopt、NOOP filter、Incremental FS 和 compiler filter。 - 安装成功但 dexopt warning:查看
DexoptResult和InstallRequest.onDexoptFinished。 - 后台 Job 未出现:确认
initializeArtManagerLocal注册的 LOCKED_BOOT_COMPLETED 回调已触发。 - dump 为空或报包不存在:检查 filtered snapshot 和安装/卸载并发时序。
11. 源码阅读路线
PackageManagerService构造和initializeArtManagerLocal:确认 ART manager 注册时机。performPackageDexOptUpgradeIfNeeded:启动 reason 选择。getBcpApexes/hasBcpApexesChanged:BCP 依赖检测。getDexoptParamsByInstallRequest:安装 reason、priority、filter 和 profile flags。shouldCallArtService/shouldSkipDexopt:跳过调用与 NOOP 的区别。performDexoptIfNeededAsync/dexoptPackageUsingArtService:异步执行和 filtered snapshot。DexoptDoneHandler/dumpDexoptState:结果消费者和诊断入口。
DexOptHelper 的核心价值是把 PMS 的包生命周期转换成 ART Service 可以消费的 reason、参数和快照,并在编译成功、失败、跳过和初始化异常之间保持清晰边界。理解这些入口后,才能继续阅读 PackageDexOptimizer 和 compiler filter 的细节。
