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 Service jest częścią modułu ART i możesz ją dostosowywać za pomocą właściwości systemowych oraz interfejsów API.

Właściwości systemowe

Usługa ART obsługuje wszystkie odpowiednie opcje dex2oat.

Dodatkowo usługa ART obsługuje te właściwości systemu:

pm.dexopt.<reason>

Jest to zbiór właściwości systemowych, które określają domyślne filtry kompilatora dla wszystkich wstępnie zdefiniowanych przyczyn kompilacji opisanych w scenariuszach Dexopt.

Więcej informacji znajdziesz w artykule 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.

Zasadniczo usługa ART wykonuje kompilację kierowaną przez profil (speed-profile) we wszystkich aplikacjach, gdy jest to możliwe, zwykle podczas optymalizacji dex 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ą korzystać z profili lokalnych.

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 Service używa filtra kompilatora określonego przez pm.dexopt.shared.

Jeśli żądana kompilacja nie jest kierowana przez profil, ta właściwość nie ma żadnego wpływu.

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

Liczba wywołań dex2oat z powodu określonych wstępnie zdefiniowanych powodów kompilacji (first-boot, boot-after-ota, boot-after-mainline-updatebg-dexopt).

Pamiętaj, że działanie tej opcji jest łączone z opcjami użycia zasobów dex2oat (dalvik.vm.*dex2oat-threads, dalvik.vm.*dex2oat-cpu-set i profilami zadań):

  • dalvik.vm.*dex2oat-threads określa liczbę wątków dla każdego wywołania dex2oat, a pm.dexopt.<reason>.concurrency – liczbę wywołań dex2oat. Oznacza to, że maksymalna liczba wątków równoczesnych jest iloczynem tych dwóch właściwości systemu.
  • Zasób dalvik.vm.*dex2oat-cpu-set i profile zadań zawsze wiąże się z wykorzystaniem rdzeni procesora niezależnie od maksymalnej liczby wątków równoczesnych (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 lepiej wykorzystać rdzenie procesora, aby przyspieszyć ogólny postęp dexopt. Jest to szczególnie przydatne podczas uruchamiania.

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 liczba ta powinna być dokładnie dostosowywana do poszczególnych produktów.

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

Jeśli ta opcja jest włączona, usługa ART pobiera tylko aplikacje używane w ciągu ostatnich dni.

Jeśli ponadto ilość wolnego miejsca jest niewielka, podczas dexoptowania w tle usługa ART Service obniża poziom kompilatora w przypadku aplikacji, których nie używano w ciągu ostatniej podanej liczby dni, aby zwolnić miejsce. Powód tego jest taki, że kompilator inactive, a filtr kompilatora jest określany przez pm.dexopt.inactive. Próg miejsca, który powoduje uruchomienie tej funkcji, to próg niskiego miejsca w Menedżerze miejsca na dysku (można go skonfigurować w ustawieniach globalnych sys_storage_threshold_percentage i sys_storage_threshold_max_bytes, domyślnie 500 MB) plus 500 MB.

Jeśli dostosujesz listę pakietów za pomocą ArtManagerLocal#setBatchDexoptStartCallback, pakiety na liście udostępnionej przez BatchDexoptStartCallback dla: bg-dexopt nigdy nie zostaną przełączone na starszą wersję.

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

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

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

Zalecana sekwencja poleceń, która zapobiega uruchamianiu zadania dexopt w tle:

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

Pierwszy wiersz zapobiega planowaniu zadania dexopt w tle, jeśli nie zostało jeszcze zaplanowane. 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 ART Service API

Usługa ART udostępnia interfejsy API w Javie do dostosowania. Interfejsy API są zdefiniowane w pliku 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. W jego znalezieniu pomaga funkcja pomocnicza com.android.server.pm.DexOptHelper#getArtManagerLocal.

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

Większość interfejsów API wymaga instancji PackageManagerLocal.FilteredSnapshot, w której przechowywane są informacje o wszystkich aplikacjach. Aby ją uzyskać, wywołaj PackageManagerLocal#withFilteredSnapshot, gdzie PackageManagerLocal to również singleton w posiadaniu LocalManagerRegistry, który można 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 dexopt. 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ł anulowania, co pozwoli w pewnym momencie anulować operację. Może to być przydatne, gdy uruchamiasz dexopt asynchronicznie.

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ć dexopt w tle, który jest inicjowany przez usługę ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Pobieranie wyników dexopt

Jeśli operacja jest inicjowana przez wywołanie funkcji dexoptPackage, możesz uzyskać wynik z wartości zwracanej.

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 odsłuchać wszystkie wyniki dexopt, niezależnie od tego, czy operacja została zainicjowana przez wywołanie dexoptPackage czy przez usługę ART, użyj polecenia 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, który wykonuje dexopt, użyj Runnable::run. Jeśli nie chcesz, aby wywołanie blokowało dexopt, użyj asynchronicznego wykonawcy.

Możesz dodać wiele wywołań zwrotnych, 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 odwołanie do niego podczas dodawania i użyj 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);

Dostosowywanie listy pakietów i parametrów 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ć, a nawet używać zupełnie innej listy.

W przypadku wywołania zwrotnego należy zignorować nieznane przyczyny, ponieważ w przyszłości może być ich więcej.

Możesz ustawić maksymalnie 1 BatchDexoptStartCallback. Wywołanie zwrotne pozostanie aktywne we wszystkich przyszłych połączeniach, chyba że je 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 optymalizacji dexopt w tle jest wykonywane raz dziennie, gdy urządzenie jest nieaktywne i ładowane. Możesz to zmienić za pomocą ArtManagerLocal#setScheduleBackgroundDexoptJobCallback.

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

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

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

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Tymczasowe wyłączenie dexopt

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

Jeśli anulujesz operację deksoptowania w tle, zostanie ona podjęta zgodnie z domyślną zasadą ponowienia próby (30 sekund, wykładnicze, ograniczenie do 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ć BatchDexoptStartCallback do dostosowywania listy pakietów lub parametrów dexopt, musisz połączyć kod w jedno wywołanie zwrotne.

// 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 inicjowany przez menedżera pakietów za pomocą wywołania dexoptPackage. 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ć wartość originalCompilerFilter.

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

Możesz ustawić tylko 1 wartość AdjustCompilerFilterCallback. Jeśli chcesz użyć parametru AdjustCompilerFilterCallback do zastąpienia filtra kompilatora w przypadku wielu pakietów, musisz połączyć kod w jednym wywołaniu zwrotnym. Połączenie zwrotne pozostaje aktywne w przypadku wszystkich przyszłych połączeń, chyba że je anulujesz.

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

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Inne dostosowania

Usługa ART obsługuje też inne modyfikacje.

Ustawianie progu temperatury dla dexopt w tle

Kontrolę termiczną zadania deksoptowania w tle jest wykonywana przez algorytm szeregowania zadań. Zadania są anulowane natychmiast po osiągnięciu temperatury THERMAL_STATUS_MODERATE. Próg THERMAL_STATUS_MODERATE można dostosować.

Sprawdzanie, czy dexopt działa w tle

Zadanie dexopt w tle jest zarządzane przez algorytm szeregowania zadań, a jego identyfikator zadania 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 do dexopt

Aby użyć profilu do kierowania dexopt, umieść plik .prof lub .dm obok pliku 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 optymalizacji dexopt, uruchom dexopt z parametrem 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}

Typowe przyczyny, 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 odpowiadają sumom kontrolnym plików .dex w pakiecie APK).