ART-Dienstkonfiguration

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