Bevor Sie beginnen, sehen Sie sich eine allgemeine Übersicht über den ART-Dienst an.
Ab Android 14 wird die On-Device-AOT-Kompilierung für Apps (auch als „dexopt“ bezeichnet) vom ART-Dienst übernommen. Der ART-Dienst ist Teil des ART-Moduls und kann über Systemeigenschaften und APIs angepasst werden.
Systemattribute
Der ART-Dienst unterstützt alle relevanten dex2oat-Optionen.
Außerdem unterstützt ART Service die folgenden Systemeigenschaften:
pm.dexopt.<reason>
Dies ist eine Reihe von Systemeigenschaften, die die Standard-Compilerfilter für alle vordefinierten Kompilierungsgründe bestimmen, die unter Dexopt-Szenarien beschrieben werden.
Weitere Informationen finden Sie unter Compiler-Filter.
Die Standardwerte sind:
pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify
pm.dexopt.shared (Standard: speed)
Dies ist der Fallback-Compilerfilter für Apps, die von anderen Apps verwendet werden.
Grundsätzlich führt ART Service die profilgesteuerte Kompilierung (speed-profile
) nach Möglichkeit für alle Apps durch, in der Regel während der Hintergrund-Dexoptimierung. Es gibt jedoch einige Apps, die von anderen Apps verwendet werden (entweder über <uses-library>
oder dynamisch über Context#createPackageContext
mit CONTEXT_INCLUDE_CODE
geladen werden). Diese Apps können aus Datenschutzgründen keine lokalen Profile verwenden.
Wenn für eine solche App die profilgesteuerte Kompilierung angefordert wird, versucht ART Service zuerst, ein Cloud-Profil zu verwenden. Wenn kein Cloud-Profil vorhanden ist, verwendet der ART-Dienst den von pm.dexopt.shared
angegebenen Compilerfilter.
Wenn die angeforderte Kompilierung nicht profilbasiert ist, hat diese Property keine Auswirkungen.
pm.dexopt.<reason>.concurrency (Standard: 1)
Dies ist die Anzahl der dex2oat-Aufrufe für bestimmte vordefinierte Kompilierungsgründe (first-boot
, boot-after-ota
, boot-after-mainline-update
und bg-dexopt
).
Die Auswirkungen dieser Option werden mit den Optionen für die dex2oat-Ressourcennutzung (dalvik.vm.*dex2oat-threads
, dalvik.vm.*dex2oat-cpu-set
und den Aufgabenprofilen) kombiniert:
dalvik.vm.*dex2oat-threads
steuert die Anzahl der Threads für jeden dex2oat-Aufruf, währendpm.dexopt.<reason>.concurrency
die Anzahl der dex2oat-Aufrufe steuert. Die maximale Anzahl gleichzeitiger Threads ist also das Produkt der beiden Systemeigenschaften.dalvik.vm.*dex2oat-cpu-set
und die Aufgabenprofile begrenzen immer die CPU-Kernauslastung, unabhängig von der maximalen Anzahl gleichzeitiger Threads (siehe oben).
Ein einzelner dex2oat-Aufruf nutzt möglicherweise nicht alle CPU-Kerne vollständig, unabhängig von dalvik.vm.*dex2oat-threads
. Durch Erhöhen der Anzahl der dex2oat-Aufrufe (pm.dexopt.<reason>.concurrency
) können CPU-Kerne besser genutzt werden, um den Fortschritt von dexopt zu beschleunigen. Das ist besonders beim Booten hilfreich.
Zu viele dex2oat-Aufrufe können jedoch dazu führen, dass auf dem Gerät der Arbeitsspeicher ausgeht. Dies lässt sich vermeiden, indem Sie dalvik.vm.dex2oat-swap
auf true
setzen, um die Verwendung einer Auslagerungsdatei zu ermöglichen. Zu viele Aufrufe können auch zu unnötigen Kontextwechseln führen. Daher sollte diese Zahl für jedes Produkt sorgfältig angepasst werden.
pm.dexopt.downgrade_after_inactive_days (Standard: nicht festgelegt)
Wenn diese Option festgelegt ist, optimiert der ART-Dienst nur Apps, die in den letzten angegebenen Tagen verwendet wurden.
Wenn der Speicherplatz fast erschöpft ist, stuft der ART-Dienst den Compilerfilter von Apps, die in den letzten Tagen nicht verwendet wurden, während der Hintergrund-Dexoptimierung herab, um Speicherplatz freizugeben. Der Compiler-Grund dafür ist inactive
und der Compiler-Filter wird durch pm.dexopt.inactive
bestimmt. Der Speicherplatzschwellenwert, der diese Funktion auslöst, ist der Schwellenwert für wenig Speicherplatz des Storage Managers (konfigurierbar über die globalen Einstellungen sys_storage_threshold_percentage
und sys_storage_threshold_max_bytes
, Standard: 500 MB) plus 500 MB.
Wenn Sie die Liste der Pakete über ArtManagerLocal#setBatchDexoptStartCallback
anpassen, werden die Pakete in der von BatchDexoptStartCallback
für bg-dexopt
bereitgestellten Liste nie auf eine niedrigere Version zurückgestuft.
pm.dexopt.disable_bg_dexopt (Standard: false)
Dies ist nur ein Test. Dadurch wird verhindert, dass der ART-Dienst den Dexopt-Job im Hintergrund plant.
Wenn der Hintergrund-Dexopt-Job bereits geplant, aber noch nicht ausgeführt wurde, hat diese Option keine Auswirkungen. Der Job wird also weiterhin ausgeführt.
Eine empfohlene Befehlsfolge, um zu verhindern, dass der Hintergrund-Dexopt-Job ausgeführt wird, ist:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Die erste Zeile verhindert, dass der Hintergrundjob „dexopt“ geplant wird, wenn er noch nicht geplant ist. In der zweiten Zeile wird der Hintergrund-Dexopt-Job abgemeldet, falls er bereits geplant ist, und sofort abgebrochen, falls er ausgeführt wird.
ART Service APIs
Der ART-Dienst stellt Java-APIs für die Anpassung bereit. Die APIs werden in ArtManagerLocal
definiert. Weitere Informationen finden Sie in der Javadoc unter art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
(Android 14-Quellcode, nicht veröffentlichte Entwicklungsquelle).
ArtManagerLocal
ist ein Singleton, das von LocalManagerRegistry
gehalten wird. Eine Hilfsfunktion com.android.server.pm.DexOptHelper#getArtManagerLocal
hilft Ihnen dabei.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
Für die meisten APIs ist eine Instanz von PackageManagerLocal.FilteredSnapshot
erforderlich, die die Informationen aller Apps enthält. Sie können sie abrufen, indem Sie PackageManagerLocal#withFilteredSnapshot
aufrufen. PackageManagerLocal
ist auch ein Singleton, das von LocalManagerRegistry
gehalten wird und über com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal
abgerufen werden kann.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
Im Folgenden sind einige typische Anwendungsfälle der APIs aufgeführt.
dexopt für eine App auslösen
Sie können dexopt für jede App jederzeit auslösen, indem Sie ArtManagerLocal#dexoptPackage
aufrufen.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
Sie können auch einen eigenen dexopt-Grund übergeben. In diesem Fall müssen die Prioritätsklasse und der Compilerfilter explizit festgelegt werden.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder("my-reason")
.setCompilerFilter("speed-profile")
.setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
.build());
}
dexopt abbrechen
Wenn ein Vorgang durch einen dexoptPackage
-Aufruf initiiert wird, können Sie ein Abbruchsignal übergeben, mit dem Sie den Vorgang zu einem bestimmten Zeitpunkt abbrechen können. Das kann nützlich sein, wenn Sie „dexopt“ asynchron ausführen.
Executor executor = ...; // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
cancellationSignal);
}
});
// When you want to cancel the operation.
cancellationSignal.cancel();
Sie können auch den von ART Service initiierten Dexopt-Vorgang im Hintergrund abbrechen.
getArtManagerLocal().cancelBackgroundDexoptJob();
dexopt-Ergebnisse abrufen
Wenn ein Vorgang durch einen dexoptPackage
-Aufruf initiiert wird, können Sie das Ergebnis aus dem Rückgabewert abrufen.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
Der ART-Dienst initiiert auch selbst in vielen Szenarien dexopt-Vorgänge, z. B. bei der Hintergrundoptimierung. Wenn Sie alle dexopt-Ergebnisse abrufen möchten, unabhängig davon, ob der Vorgang durch einen dexoptPackage
-Aufruf oder durch den ART-Dienst initiiert wurde, verwenden Sie ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Mit dem ersten Argument wird festgelegt, ob nur Aktualisierungen im Ergebnis enthalten sein sollen. Wenn Sie nur Pakete anhören möchten, die von dexopt aktualisiert werden, setzen Sie den Wert auf „true“.
Das zweite Argument ist der Executor des Callbacks. Wenn der Callback im selben Thread wie „dexopt“ ausgeführt werden soll, verwenden Sie Runnable::run
. Wenn Sie nicht möchten, dass der Callback „dexopt“ blockiert, verwenden Sie einen asynchronen Executor.
Sie können mehrere Callbacks hinzufügen. Der ART-Dienst führt sie alle nacheinander aus. Alle Callbacks bleiben für alle zukünftigen Anrufe aktiv, sofern Sie sie nicht entfernen.
Wenn Sie einen Callback entfernen möchten, behalten Sie die Referenz des Callbacks bei, wenn Sie ihn hinzufügen, und verwenden Sie ArtManagerLocal#removeDexoptDoneCallback
.
DexoptDoneCallback callback = (result) -> {
// Process the result here.
...
};
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */, Runnable::run, callback);
// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);
Paketliste und dexopt-Parameter anpassen
Der ART-Dienst initiiert dexopt-Vorgänge selbst während des Bootvorgangs und der Hintergrund-Dexopt-Vorgänge. Wenn Sie die Paketliste oder die dexopt-Parameter für diese Vorgänge anpassen möchten, verwenden Sie ArtManagerLocal#setBatchDexoptStartCallback
.
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
switch (reason) {
case ReasonMapping.REASON_BG_DEXOPT:
var myPackages = new ArrayList<String>(defaultPackages);
myPackages.add(...);
myPackages.remove(...);
myPackages.sort(...);
builder.setPackages(myPackages);
break;
default:
// Ignore unknown reasons.
}
});
Sie können der Paketliste Elemente hinzufügen, Elemente daraus entfernen, sie sortieren oder eine ganz andere Liste verwenden.
In Ihrem Callback müssen unbekannte Gründe ignoriert werden, da in Zukunft weitere Gründe hinzugefügt werden können.
Sie können höchstens einen BatchDexoptStartCallback
festlegen. Der Rückruf bleibt für alle zukünftigen Anrufe aktiv, bis Sie ihn löschen.
Wenn Sie den Callback löschen möchten, verwenden Sie ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Parameter des Hintergrund-Dexopt-Jobs anpassen
Standardmäßig wird der Hintergrund-Dexopt-Job einmal täglich ausgeführt, wenn das Gerät im Leerlauf ist und geladen wird. Das lässt sich mit ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
ändern.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Sie können höchstens einen ScheduleBackgroundDexoptJobCallback
festlegen. Der Rückruf bleibt für alle zukünftigen Anrufe aktiv, bis Sie ihn löschen.
Wenn Sie den Callback löschen möchten, verwenden Sie ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
dexopt vorübergehend deaktivieren
Jeder dexopt-Vorgang, der vom ART-Dienst initiiert wird, löst eine BatchDexoptStartCallback
aus. Sie können die Vorgänge immer wieder abbrechen, um dexopt effektiv zu deaktivieren.
Wenn der Vorgang, den Sie abbrechen, „background dexopt“ ist, wird die Standardrichtlinie für Wiederholungsversuche (30 Sekunden, exponentiell, maximal 5 Stunden) angewendet.
// Good example.
var shouldDisableDexopt = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (shouldDisableDexopt.get()) {
cancellationSignal.cancel();
}
});
// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();
// Re-enable dexopt.
shouldDisableDexopt.set(false);
Sie können höchstens ein BatchDexoptStartCallback
haben. Wenn Sie BatchDexoptStartCallback
auch verwenden möchten, um die Paketliste oder die dexopt-Parameter anzupassen, müssen Sie den Code in einem Callback kombinieren.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
Der dexopt-Vorgang, der bei der App-Installation ausgeführt wird, wird nicht von ART Service initiiert. Stattdessen wird sie vom Paketmanager über einen dexoptPackage
-Aufruf initiiert. Daher wird BatchDexoptStartCallback
nicht ausgelöst. Wenn Sie „dexopt“ bei der App-Installation deaktivieren möchten, verhindern Sie, dass der Paketmanager dexoptPackage
aufruft.
Compilerfilter für bestimmte Pakete überschreiben (Android 15 und höher)
Sie können den Compilerfilter für bestimmte Pakete überschreiben, indem Sie über setAdjustCompilerFilterCallback
einen Callback registrieren. Der Callback wird immer dann aufgerufen, wenn ein Paket dexoptimiert wird, unabhängig davon, ob die Dexoptimierung vom ART-Dienst während des Bootvorgangs und der Hintergrund-Dexoptimierung oder durch einen dexoptPackage
-API-Aufruf initiiert wird.
Wenn ein Paket nicht angepasst werden muss, muss im Callback originalCompilerFilter
zurückgegeben werden.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
Sie können nur eine AdjustCompilerFilterCallback
festlegen. Wenn Sie AdjustCompilerFilterCallback
verwenden möchten, um den Compilerfilter für mehrere Pakete zu überschreiben, müssen Sie den Code in einem Callback kombinieren. Der Rückruf bleibt für alle zukünftigen Anrufe aktiv, bis Sie ihn löschen.
Wenn Sie den Callback löschen möchten, verwenden Sie ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Weitere Anpassungen
Der ART-Dienst unterstützt auch einige andere Anpassungen.
Thermischen Grenzwert für die Hintergrund-Dexopt festlegen
Die thermische Steuerung des Hintergrund-Dexopt-Jobs erfolgt durch Job Scheduler.
Der Job wird sofort abgebrochen, wenn die Temperatur THERMAL_STATUS_MODERATE
erreicht. Der Grenzwert von THERMAL_STATUS_MODERATE
kann angepasst werden.
Prüfen, ob „dexopt“ im Hintergrund ausgeführt wird
Der Hintergrund-Dexopt-Job wird von Job Scheduler verwaltet und hat die Job-ID 27873780
. Verwenden Sie Job Scheduler APIs, um festzustellen, ob der Job ausgeführt wird.
// Good example.
var jobScheduler =
Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);
if (reason == PENDING_JOB_REASON_EXECUTING) {
// Do something when the job is running.
...
}
// Bad example.
var backgroundDexoptRunning = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(true);
}
});
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(false);
}
});
if (backgroundDexoptRunning.get()) {
// Do something when the job is running.
...
}
Profil für dexopt bereitstellen
Wenn Sie ein Profil verwenden möchten, um dexopt zu steuern, legen Sie eine .prof
-Datei oder eine .dm
-Datei neben das APK.
Die Datei .prof
muss eine Profildatei im Binärformat sein und der Dateiname muss dem Dateinamen des APKs + .prof
entsprechen. Beispiel:
base.apk.prof
Der Dateiname der .dm
-Datei muss dem Dateinamen des APK entsprechen, wobei die Erweiterung durch .dm
ersetzt wird. Beispiel:
base.dm
Wenn Sie prüfen möchten, ob das Profil für dexopt verwendet wird, führen Sie dexopt mit speed-profile
aus und prüfen Sie das Ergebnis.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
In der ersten Zeile werden alle Profile gelöscht, die von der Laufzeitumgebung erstellt wurden (d.h. die Profile in /data/misc/profiles
), um sicherzustellen, dass das Profil neben dem APK das einzige Profil ist, das vom ART-Dienst verwendet werden kann. In der zweiten Zeile wird „dexopt“ mit speed-profile
ausgeführt und -v
übergeben, um das ausführliche Ergebnis auszugeben.
Wenn das Profil verwendet wird, sehen Sie actualCompilerFilter=speed-profile
im Ergebnis. Andernfalls wird actualCompilerFilter=verify
angezeigt. Beispiel:
DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}
Typische Gründe dafür, dass der ART-Dienst das Profil nicht verwendet:
- Das Profil hat einen falschen Dateinamen oder befindet sich nicht neben dem APK.
- Das Profil hat das falsche Format.
- Das Profil stimmt nicht mit dem APK überein. Die Prüfsummen im Profil stimmen nicht mit den Prüfsummen der
.dex
-Dateien im APK überein.