Konfiguracja usługi ART

Zanim zaczniesz, zapoznaj się z ogólnymi informacjami o usłudze ART.

Począwszy od Androida 14 kompilacja AOT na urządzeniu w przypadku aplikacji (czyli dexopt) jest obsługiwana przez usługę ART. Usługa ART jest częścią modułu ART i można ją dostosować za pomocą właściwości systemowych oraz interfejsów API.

Właściwości systemowe

Usługa ART obsługuje wszystkie dex2oat.

Dodatkowo ART Service obsługuje te właściwości systemowe:

pm.dexopt.<reason>

To jest zestaw właściwości systemowych, które określają domyślne filtry kompilatora ze wszystkich wstępnie zdefiniowanych powodów kompilacji opisanych w scenariuszach Dexopt.

Więcej informacji: Filtry kompilatora.

Standardowe wartości domyślne:

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 (domyślnie: szybkość)

Jest to domyślny filtr kompilatora dla aplikacji używanych przez inne aplikacje.

Z reguły usługa ART tworzy kompilację zgodną z profilem (speed-profile) dla: wszystkich aplikacji, gdy jest to możliwe, zwykle podczas deksoptowania w tle. Są jednak aplikacje, których używają inne aplikacje (poprzez <uses-library> lub wczytywane dynamicznie za pomocą Context#createPackageContextCONTEXT_INCLUDE_CODE). Z powodu prywatności takie aplikacje nie mogą używać lokalnych profili.

Jeśli w przypadku takiej aplikacji wymagana jest kompilacja z użyciem profilu, usługa ART najpierw próbuje użyć profilu w chmurze. Jeśli profil chmury nie istnieje, usługa ART używa filtra kompilatora określonego przez pm.dexopt.shared.

Jeśli żądana kompilacja nie jest obsługiwana przez profil, ta właściwość nie powoduje żadnych skutków.

pm.dexopt.<reason>.concurrency (domyślnie: 1)

To jest liczba wywołań dex2oat dla określonej wstępnie zdefiniowanej kompilacji przyczyny (first-boot, boot-after-ota, boot-after-mainline-update i bg-dexopt).

Pamiętaj, że efekt tej opcji jest połączony z opcje wykorzystania zasobów dex2oat (dalvik.vm.*dex2oat-threads, dalvik.vm.*dex2oat-cpu-set i profili zadań):

  • dalvik.vm.*dex2oat-threads określa liczbę wątków dla każdego wywołania dex2oat, a pm.dexopt.<reason>.concurrency określa liczbę wywołań dex2oat. Oznacza to, że maksymalna liczba wątków równoczesnych jest iloczynem tych dwóch właściwości systemu.
  • Procesor dalvik.vm.*dex2oat-cpu-set i profile zadań zawsze są powiązane z rdzeniem procesora bez względu na maksymalną liczbę równoczesnych wątków (omówione powyżej).

Pojedyncze wywołanie dex2oat może nie wykorzystać w pełni wszystkich rdzeni procesora, niezależnie od dalvik.vm.*dex2oat-threads. Dlatego zwiększenie liczby wywołań dex2oat (pm.dexopt.<reason>.concurrency) może pozwolić na lepsze wykorzystanie rdzeni procesora i przyspieszenie ogólnego postępu dexopt. Jest to szczególnie przydatne podczas uruchamianie.

Jednak zbyt duża liczba wywołań dex2oat może spowodować wyczerpanie się pamięci na urządzeniu, nawet jeśli można to ograniczyć, ustawiając dalvik.vm.dex2oat-swap na true, aby umożliwić używanie pliku wymiany. Zbyt wiele wywołań może też powodować niepotrzebne przełączanie kontekstu. Dlatego należy go dokładnie dostosować w poszczególnych usługach.

pm.dexopt.downgrade_after_inactive_days (domyślnie: nie ustawiono)

Jeśli ta opcja jest ustawiona, usługa ART dezaktywuje tylko aplikacje używane w ciągu podanej liczby dni.

Ponadto, jeśli ilość miejsca na dane jest prawie niewystarczająca, podczas deksoptowania w tle usługa ART obniża filtr kompilatora dla aplikacji, które nie są używane w ciągu ostatnich liczby dni, aby zwolnić miejsce. Powód podany przez kompilator to inactive, a filtr kompilatora jest określany przez pm.dexopt.inactive. Próg miejsca na dane, który powoduje uruchomienie tej funkcji, to próg niskiego miejsca na dane w Menedżerze miejsca na dane (można go skonfigurować w ustawieniach globalnych sys_storage_threshold_percentagesys_storage_threshold_max_bytes, domyślnie 500 MB) plus 500 MB.

Jeśli dostosujesz listę pakietów na stronie ArtManagerLocal#setBatchDexoptStartCallback, pakiety na liście podanej od BatchDexoptStartCallback dla bg-dexopt nigdy nie są obniżane.

pm.dexopt.disable_bg_dexopt (domyślnie: false)

Jest to tylko test. Zapobiega to zaplanowaniu przez usługę ART zadania dexopt w tle.

Jeśli zadanie dexopt w tle jest już zaplanowane, ale nie zostało jeszcze uruchomione, nie ma żadnego efektu. Oznacza to, że zadanie nadal będzie wykonywane.

Zalecana sekwencja poleceń, które uniemożliwiają zadanie deksoptowania w tle uruchomiony jest:

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

Pierwszy wiersz zapobiega zaplanowaniu zadania dexopt w tle, jeśli nie zaplanowano. Drugi wiersz odwołuje zaplanowane zadanie dexopt w tle, jeśli jest już zaplanowane, i natychmiast anuluje zadanie dexopt w tle, jeśli jest uruchomione.

Interfejsy API usługi ART

Usługa ART udostępnia interfejsy API Javy do dostosowywania. Interfejsy API zostały zdefiniowane tutaj: ArtManagerLocal Zapoznaj się z dokumentacją Javadoc w pliku art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java, aby dowiedzieć się, jak korzystać z funkcji (kod źródłowy Androida 14, niepublikowany kod źródłowy w wersji rozwojowej).

ArtManagerLocal to singleton posiadany przez: LocalManagerRegistry. Funkcja pomocnicza com.android.server.pm.DexOptHelper#getArtManagerLocal pomaga Ci ją uzyskać.

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

Większość interfejsów API wymaga instancji PackageManagerLocal.FilteredSnapshot, który zawiera informacje o wszystkich aplikacjach. Można go uzyskać, wywołując funkcję PackageManagerLocal#withFilteredSnapshot, gdzie PackageManagerLocal jest również singletonem przechowywanym przez LocalManagerRegistry i można go uzyskać z com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal.

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

Poniżej podano kilka typowych przypadków użycia interfejsów API.

Uruchamianie dexopt w aplikacji

W dowolnym momencie możesz wywołać dexopt dla dowolnej aplikacji, wywołując funkcję ArtManagerLocal#dexoptPackage.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

Możesz też podać własny powód deksoptowania. Jeśli to zrobisz, klasa priorytetowa i filtr kompilatora muszą być ustawione w sposób jawny.

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());
}

Anulowanie dexopt

Jeśli operacja jest inicjowana przez wywołanie dexoptPackage, możesz przekazać sygnału anulowania, który pozwala w pewnym momencie anulować operację. Może to spowodować może być przydatne przy asynchronicznym uruchamianiu narzędzia dexopt.

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();

Możesz też anulować dexopting w tle inicjowany przez usługę ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Pobieranie wyników dexopt

Jeśli operacja została zainicjowana przez wywołanie dexoptPackage, możesz uzyskać wynik od zwracanej wartości.

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

Usługa ART Service inicjuje też samodzielnie operacje dexopt w wielu scenariuszach, np. dexopt w tle. Aby nasłuchiwać wszystkich wyników deksoptowania, niezależnie od tego, czy operacja zainicjowane przez wywołanie dexoptPackage lub przez usługę ART, użyj ArtManagerLocal#addDexoptDoneCallback

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

Pierwszy argument określa, czy w wyniku mają być uwzględniane tylko aktualizacje. Jeśli chcesz słuchać tylko pakietów aktualizowanych przez dexopt, ustaw tę opcję na wartość true.

Drugi argument jest wykonawcą wywołania zwrotnego. Aby wykonać wywołanie zwrotne w tym samym wątku, w którym przeprowadza się deksoptowanie, użyj parametru Runnable::run. Jeśli nie chcesz, aby rozszerzenie wywołanie zwrotne do zablokowania dexopt, użyj wykonawcy asynchronicznego.

Możesz dodać wiele funkcji wywołania zwrotnego, a usługa ART Service wykona je wszystkie kolejno. Wszystkie wywołania zwrotne pozostaną aktywne we wszystkich przyszłych wywołaniach, chyba że je usuniesz.

Jeśli chcesz usunąć wywołanie zwrotne, zachowaj jego odniesienie i dodać 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);

Dostosuj listę pakietów i parametry dexopt

Usługa ART inicjuje operacje dexopt samodzielnie podczas uruchamiania i w tledexopt. Aby dostosować listę pakietów lub parametry dexopt w przypadku tych operacji, użyj 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.
      }
    });

Możesz dodawać elementy do listy pakietów, usuwać je z niej, sortować ją, a nawet użyć zupełnie innej listy.

Oddzwanianie musi ignorować nieznane przyczyny, ponieważ można dodać więcej powodów przyszłości.

Możesz ustawić maksymalnie 1 wartość BatchDexoptStartCallback. Oddzwanianie pozostanie bez zmian aktywna dla wszystkich przyszłych połączeń, chyba że ją usuniesz.

Jeśli chcesz anulować wywołanie zwrotne, użyj opcji ArtManagerLocal#clearBatchDexoptStartCallback.

getArtManagerLocal().clearBatchDexoptStartCallback();

Dostosowywanie parametrów zadania dexopt wykonywanego w tle

Domyślnie zadanie deksoptowania w tle jest uruchamiane raz dziennie, gdy urządzenie jest bezczynne i ładowanie. Można to zmienić za pomocą ArtManagerLocal#setScheduleBackgroundDexoptJobCallback

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

Możesz ustawić maksymalnie 1 wartość ScheduleBackgroundDexoptJobCallback. Połączenie zwrotne będzie aktywne we wszystkich przyszłych rozmowach, chyba że je wyłączysz.

Jeśli chcesz anulować oddzwonienie, użyj ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Tymczasowe wyłączenie dexopt

Każda operacja deksoptowania zainicjowana przez usługę ART powoduje wywołanie BatchDexoptStartCallback Możesz anulować operacje, aby: skutecznie wyłączyć dexopt.

Jeśli anulowana operacja to dexopt w tle, stosuje się do niej domyślna zasada ponownych prób (30 sekund, wykładniczo, z maksimum 5 godzin).

// 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);

Możesz mieć maksymalnie 1 BatchDexoptStartCallback. Jeśli chcesz też używać parametru BatchDexoptStartCallback do dostosowywania listy pakietów lub parametrów dexopt, musisz połączyć kod w jedną funkcję wywołania zwrotnego.

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

Operacja dexopt wykonywana podczas instalacji aplikacji nie jest inicjowana przez usługę ART. Zamiast tego jest inicjowane przez menedżera pakietów za pomocą polecenia dexoptPackage połączenie. Dlatego nie uruchamia się BatchDexoptStartCallback. Aby wyłączyć dexopt podczas instalowania aplikacji, uniemożliw zarządzaniu pakietami wywoływanie funkcji dexoptPackage.

Zastępowanie filtra kompilatora w przypadku niektórych pakietów (Android 15 i nowsze)

Możesz zastąpić filtr kompilatora w przypadku niektórych pakietów, rejestrując funkcję wywołania zwrotnego za pomocą funkcji setAdjustCompilerFilterCallback. Funkcja wywołania zwrotnego jest wywoływana za każdym razem, gdy pakiet ma zostać zdexoptowany, niezależnie od tego, czy dexopt został zainicjowany przez usługę ART podczas uruchamiania i tła dexopt, czy przez wywołanie interfejsu API dexoptPackage.

Jeśli pakiet nie wymaga korekty, wywołanie zwrotne musi zwrócić originalCompilerFilter.

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

Możesz ustawić tylko jeden element AdjustCompilerFilterCallback. Jeśli chcesz użyć opcji AdjustCompilerFilterCallback, aby zastąpić filtr kompilatora w przypadku wielu pakietów, musisz połączyć kod w jedną funkcję wywołania zwrotnego. Oddzwanianie pozostaje aktywna dla wszystkich przyszłych połączeń, chyba że ją usuniesz.

Jeśli chcesz anulować wywołanie zwrotne, użyj opcji ArtManagerLocal#clearAdjustCompilerFilterCallback.

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Inne dostosowania

Usługa ART obsługuje też inne opcje dostosowywania.

Ustawianie progu temperatury dla dexopt w tle

Usługa Job Scheduler zapewnia kontrolę termiczną zadania dexopt działającego w tle. Zadanie zostanie anulowane natychmiast, gdy temperatura osiągnie THERMAL_STATUS_MODERATE Próg THERMAL_STATUS_MODERATE można dostosować.

Sprawdź, czy uruchomiona jest deksoptacja w tle

Zadanie dexopt w tle jest zarządzane przez harmonogram zadań, a jego identyfikator to 27873780. Aby sprawdzić, czy zadanie jest wykonywane, użyj interfejsów Job Scheduler API.

// 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.
  ...
}

Podaj profil dla dexopt

Aby używać profilu do prowadzenia dexopt, umieść plik .prof lub .dm obok plik APK.

Plik .prof musi być plikiem profilu w formacie binarnym, a jego nazwa musi być taka sama jak nazwa pliku APK + .prof. Na przykład

base.apk.prof

Nazwa pliku .dm musi być nazwą pliku APK z rozszerzeniem .dm. Na przykład

base.dm

Aby sprawdzić, czy profil jest używany do dexopt, uruchom dexopt za pomocą polecenia speed-profile i sprawdź wynik.

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

Pierwsza linijka usuwa wszystkie profile utworzone przez środowisko uruchomieniowe (czyli te w /data/misc/profiles), jeśli takie istnieją, aby mieć pewność, że profil obok pliku APK jest jedynym profilem, którego usługa ART może użyć. Druga linia uruchamia dexopt z parametrem speed-profile i przekazuje parametr -v, aby wydrukować szczegółowy wynik.

Jeśli profil jest używany, w wyniku zobaczysz actualCompilerFilter=speed-profile. W przeciwnym razie zobaczysz actualCompilerFilter=verify. Na przykład

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}

Oto kilka typowych powodów, dla których usługa ART nie używa profilu:

  • Profil ma nieprawidłową nazwę pliku lub nie znajduje się obok pliku APK.
  • Profil ma nieprawidłowy format.
  • Profil nie pasuje do pliku APK. (Sumy kontrolne w profilu nie pasują do sum kontrolnych plików .dex w pakiecie APK).