Bevor Sie beginnen, sehen Sie sich einen allgemeinen Überblick über den ART-Dienst an.
Ab Android 14 wird die On-Device-AOT-Kompilierung für Apps (auch als dexopt bezeichnet) vom ART-Dienst ausgeführt. Der ART-Dienst ist Teil des ART-Moduls und lässt sich über Systemeigenschaften und APIs anpassen.
Systemeigenschaften
Der ART-Dienst unterstützt alle relevanten dex2oat-Optionen.
Außerdem unterstützt der ART-Dienst die folgenden Systemeigenschaften:
pm.dexopt.<reason>
Dies ist eine Reihe von Systemeigenschaften, die die Standardcompilerfilter für alle vordefinierten Kompilierungsgründe festlegen, die in Dexopt-Szenarien beschrieben sind.
Weitere Informationen finden Sie unter Compilerfilter.
Die Standardwerte lauten:
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: Geschwindigkeit)
Dies ist der Fallback-Compilerfilter für Apps, die von anderen Apps verwendet werden.
Im Prinzip führt der ART-Dienst nach Möglichkeit eine profilbasierte Kompilierung (speed-profile
) für alle Apps durch, in der Regel während der DeXOpt im Hintergrund. Es gibt jedoch einige Apps, die von anderen Apps verwendet werden (entweder über <uses-library>
oder dynamisch mit Context#createPackageContext
über CONTEXT_INCLUDE_CODE
geladen). Solche Apps können aus Datenschutzgründen keine lokalen Profile verwenden.
Wenn für eine solche App eine profilbasierte Kompilierung angefordert wird, versucht der ART-Dienst zuerst, ein Cloud-Profil zu verwenden. Wenn kein Cloud-Profil vorhanden ist, verwendet der ART-Dienst den mit 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 aus bestimmten vordefinierten Gründen für die Kompilierung (first-boot
, boot-after-ota
, boot-after-mainline-update
und bg-dexopt
).
Die Auswirkungen dieser Option werden mit den Optionen für die Ressourcennutzung von dex2oat (dalvik.vm.*dex2oat-threads
, dalvik.vm.*dex2oat-cpu-set
und die Aufgabenprofile) kombiniert:
- Mit
dalvik.vm.*dex2oat-threads
wird die Anzahl der Threads für jeden dex2oat-Aufruf gesteuert, währendpm.dexopt.<reason>.concurrency
die Anzahl der dex2oat-Aufrufe steuert. Das heißt, die maximale Anzahl gleichzeitiger Threads ist das Produkt der beiden Systemattribute. dalvik.vm.*dex2oat-cpu-set
und die Aufgabenprofile begrenzen die CPU-Kernnutzung unabhängig von der maximalen Anzahl gleichzeitiger Threads (siehe oben).
Eine einzelne dex2oat-Aufrufung nutzt unabhängig von dalvik.vm.*dex2oat-threads
möglicherweise nicht alle CPU-Kerne vollständig aus. Durch eine Erhöhung der Anzahl der dex2oat-Aufrufe (pm.dexopt.<reason>.concurrency
) können CPU-Kerne besser genutzt werden, um den Gesamtfortschritt von dexopt zu beschleunigen. Dies ist besonders während des Startvorgangs hilfreich.
Zu viele dex2oat-Aufrufe können jedoch dazu führen, dass dem Gerät der Arbeitsspeicher ausgeht. Dies kann jedoch abgemildert werden, indem dalvik.vm.dex2oat-swap
auf true
festgelegt wird, 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 abgestimmt werden.
pm.dexopt.downgrade_after_inactive_days (Standard: nicht festgelegt)
Wenn diese Option aktiviert ist, deaktiviert der ART-Dienst nur Apps, die in den letzten angegebenen Tagen verwendet wurden.
Wenn der Speicherplatz fast aufgebraucht ist, degradiert der ART-Dienst während der De-Optimierung im Hintergrund den Compilerfilter von Apps, die in den letzten Tagen nicht verwendet wurden, um Speicherplatz freizugeben. Der Compilergrund hierfür ist inactive
und der Compilerfilter wird durch pm.dexopt.inactive
bestimmt. Der Grenzwert für die Auslösung dieser Funktion ist der Grenzwert für wenig Speicherplatz des Speichermanagers (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 Liste, die BatchDexoptStartCallback
für bg-dexopt
zur Verfügung stellt, nie herabgestuft.
pm.dexopt.disable_bg_dexopt (Standard: false)
Diese Funktion ist nur für Tests vorgesehen. Dadurch wird verhindert, dass der ART-Dienst den Deopt-Job im Hintergrund plant.
Wenn der dexopt-Job im Hintergrund bereits geplant, aber noch nicht ausgeführt wurde, hat diese Option keine Auswirkungen. Das heißt, der Job wird weiterhin ausgeführt.
Eine empfohlene Abfolge von Befehlen, um die Ausführung des Jobs zum Entfernen im Hintergrund zu verhindern, ist:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Mit der ersten Zeile wird verhindert, dass der dexopt-Job im Hintergrund geplant wird, falls er noch nicht geplant ist. In der zweiten Zeile wird die Planung des dexopt-Jobs im Hintergrund aufgehoben, falls er bereits geplant ist, und er wird sofort abgebrochen, falls er gerade ausgeführt wird.
ART Service APIs
Der ART-Dienst stellt Java APIs für die Anpassung bereit. Die APIs sind in ArtManagerLocal
definiert. Informationen zur Verwendung finden Sie im Javadoc in art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
(Android 14-Quelle, unveröffentlichte Entwicklungsquelle).
ArtManagerLocal
ist ein Singleton, der von LocalManagerRegistry
verwaltet wird. Die 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 zu allen Anwendungen enthält. Sie können sie abrufen, indem Sie PackageManagerLocal#withFilteredSnapshot
aufrufen, wobei PackageManagerLocal
auch ein Singleton-Element ist, das von LocalManagerRegistry
verwaltet wird und von 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 jederzeit für eine beliebige App 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 Grund für die Deaktivierung von Dexopt angeben. 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 gestartet wird, kannst du ein Abbruchsignal übergeben, mit dem du den Vorgang jederzeit abbrechen kannst. Das kann hilfreich 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 die Deaktivierung von dexopt im Hintergrund abbrechen, die vom ART-Dienst initiiert wird.
getArtManagerLocal().cancelBackgroundDexoptJob();
Dexopt-Ergebnisse erhalten
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 in vielen Szenarien auch selbst Dexopt-Vorgänge, z. B. die Hintergrunddexopt-Funktion. Wenn Sie alle Dexopt-Ergebnisse überwachen möchten, unabhängig davon, ob der Vorgang durch einen dexoptPackage
-Aufruf oder vom ART Service initiiert wird, verwenden Sie ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Mit dem ersten Argument wird festgelegt, ob nur Aktualisierungen in das Ergebnis eingeschlossen werden sollen. Wenn Sie nur Pakete überwachen möchten, die von dexopt aktualisiert werden, setzen Sie den Wert auf „wahr“.
Das zweite Argument ist der Ausführende des Callbacks. Wenn Sie den Rückruf in demselben Thread ausführen möchten, in dem dexopt ausgeführt wird, verwenden Sie Runnable::run
. Wenn der Rückruf dexopt nicht blockieren soll, verwenden Sie einen asynchronen Executor.
Sie können mehrere Callbacks hinzufügen. Der ART-Dienst führt sie dann nacheinander aus. Alle Callbacks bleiben für alle zukünftigen Anrufe aktiv, es sei denn, Sie entfernen sie.
Wenn Sie einen Rückruf entfernen möchten, bewahren Sie die Referenz des Rückrufs beim Hinzufügen auf 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 startet die DeXOpt-Vorgänge selbst beim Starten und im Hintergrund. Verwenden Sie ArtManagerLocal#setBatchDexoptStartCallback
, um die Paketliste oder dexopt-Parameter für diese Vorgänge anzupassen.
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 sogar eine ganz andere Liste verwenden.
Unbekannte Gründe müssen vom Callback ignoriert werden, da in Zukunft weitere Gründe hinzugefügt werden können.
Sie können maximal einen BatchDexoptStartCallback
festlegen. Der Callback bleibt für alle zukünftigen Anrufe aktiv, es sei denn, Sie löschen ihn.
Wenn Sie den Rückruf löschen möchten, verwenden Sie ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Parameter des dexopt-Jobs im Hintergrund anpassen
Standardmäßig wird der dexopt-Job im Hintergrund einmal täglich ausgeführt, wenn das Gerät inaktiv ist und geladen wird. Dies kann mit ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
geändert werden.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Sie können maximal einen ScheduleBackgroundDexoptJobCallback
festlegen. Der Rückruf bleibt für alle zukünftigen Anrufe aktiv, es sei denn, Sie löschen ihn.
Wenn Sie den Rückruf 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 einen BatchDexoptStartCallback
aus. Sie können die Vorgänge so lange abbrechen, bis dexopt deaktiviert ist.
Wenn der abgebrochene Vorgang die Deaktivierung von Dexopt im Hintergrund ist, wird die Standardwiederholrichtlinie (30 Sekunden, exponentiell, begrenzt auf 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 maximal eine BatchDexoptStartCallback
haben. Wenn du die Paketliste oder dexopt-Parameter auch mit BatchDexoptStartCallback
anpassen möchtest, musst du den Code in einem Callback kombinieren.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
Der bei der App-Installation durchgeführte Dexopt-Vorgang wird nicht vom ART-Service initiiert. Stattdessen wird es vom Paketmanager über einen dexoptPackage
-Aufruf initiiert. Daher wird nicht BatchDexoptStartCallback
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 Rückruf registrieren. Der Rückruf wird immer dann aufgerufen, wenn ein Paket dexoptiert wird, unabhängig davon, ob die Deaktivierung durch den ART-Dienst während des Bootens und der Deaktivierung im Hintergrund oder durch einen dexoptPackage
API-Aufruf initiiert wird.
Wenn ein Paket nicht angepasst werden muss, muss der Callback originalCompilerFilter
zurückgeben.
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 Callback bleibt für alle zukünftigen Aufrufe aktiv, sofern Sie ihn nicht löschen.
Wenn Sie den Rückruf löschen möchten, verwenden Sie ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Weitere Anpassungen
Der ART-Dienst unterstützt auch einige andere Anpassungen.
Temperaturgrenzwert für die Hintergrund-DexOpt-Optimierung festlegen
Die thermische Steuerung des dexopt-Jobs im Hintergrund erfolgt über den 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 vom Jobplaner verwaltet und seine Job-ID lautet 27873780
. Mithilfe von Job Scheduler APIs können Sie feststellen, 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 angeben
Wenn Sie ein Profil für dexopt verwenden möchten, legen Sie eine .prof
- oder .dm
-Datei neben die APK ab.
Die .prof
-Datei muss eine Profildatei im Binärformat sein und der Dateiname muss dem Dateinamen der APK-Datei mit .prof
enden. Beispiel:
base.apk.prof
Der Dateiname der .dm
-Datei muss mit dem Dateinamen der APK-Datei identisch sein, wobei die Erweiterung durch .dm
ersetzt wird. Beispiel:
base.dm
Um zu prüfen, 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 von der Laufzeit erstellten Profile (d.h. die in /data/misc/profiles
) gelöscht, falls vorhanden, damit das Profil neben dem APK das einzige Profil ist, das der ART-Dienst verwenden kann. In der zweiten Zeile wird dexopt mit speed-profile
ausgeführt und -v
übergeben, um das ausführliche Ergebnis zu drucken.
Wenn das Profil verwendet wird, wird in den Ergebnissen actualCompilerFilter=speed-profile
angezeigt. 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}
Häufige Gründe, warum ART Service das Profil nicht verwendet, sind folgende:
- 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.