ART-Dienstkonfiguration

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ährend pm.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.