Konfiguracja usługi ART

Zanim zaczniesz, zapoznaj się z ogólnym przeglądem usługi ART .

Począwszy od Androida 14, kompilacją AOT dla aplikacji (inaczej dexopt) na urządzeniu zajmuje się usługa ART. Usługa ART jest częścią modułu ART i można ją dostosować za pomocą właściwości systemu i interfejsów API.

Właściwości systemu

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

Dodatkowo ART Service obsługuje następujące właściwości systemu:

pm.dexopt.<powód>

Jest to zestaw właściwości systemowych, które określają domyślne filtry kompilatora dla wszystkich predefiniowanych powodów kompilacji opisanych w scenariuszach Dexopt .

Aby uzyskać więcej informacji, zobacz Filtry kompilatora .

Standardowe wartości domyślne to:

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: prędkość)

Jest to zastępczy filtr kompilatora dla aplikacji używanych przez inne aplikacje.

Zasadniczo usługa ART wykonuje kompilację sterowaną profilem ( speed-profile ) dla wszystkich aplikacji, jeśli to możliwe, zazwyczaj podczas deksoptu w tle. Istnieją jednak aplikacje, które są używane przez inne aplikacje (albo przez <uses-library> , albo ładowane dynamicznie przy użyciu Context#createPackageContext z CONTEXT_INCLUDE_CODE ). Takie aplikacje nie mogą korzystać z profili lokalnych ze względu na prywatność.

W przypadku takiej aplikacji, jeśli żądana jest kompilacja oparta na profilu, usługa ART najpierw próbuje użyć profilu w chmurze. Jeśli profil w chmurze nie istnieje, usługa ART ponownie użyje filtra kompilatora określonego przez pm.dexopt.shared .

Jeśli żądana kompilacja nie jest oparta na profilu, ta właściwość nie ma żadnego efektu.

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

Jest to liczba wywołań dex2oat z określonych, predefiniowanych powodów kompilacji ( first-boot , boot-after-ota , boot-after-mainline-update i bg-dexopt ).

Należy pamiętać, że działanie tej opcji jest połączone z opcjami wykorzystania zasobów dex2oat ( dalvik.vm.*dex2oat-threads , dalvik.vm.*dex2oat-cpu-set i profile zadań):

  • dalvik.vm.*dex2oat-threads kontroluje liczbę wątków dla każdego wywołania dex2oat, podczas gdy pm.dexopt.<reason>.concurrency kontroluje liczbę wywołań dex2oat. Oznacza to, że maksymalna liczba współbieżnych wątków jest iloczynem dwóch właściwości systemu.
  • dalvik.vm.*dex2oat-cpu-set i profile zadań zawsze ograniczały wykorzystanie rdzenia procesora, niezależnie od maksymalnej liczby jednoczesnych 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 lepiej wykorzystać rdzenie procesora i przyspieszyć ogólny postęp dexopt. Jest to szczególnie przydatne podczas uruchamiania.

Jednakże zbyt wiele wywołań dex2oat może spowodować, że w urządzeniu zabraknie pamięci, nawet jeśli można temu zaradzić, ustawiając dalvik.vm.dex2oat-swap na true , aby umożliwić użycie pliku wymiany. Zbyt wiele wywołań może również spowodować niepotrzebne przełączanie kontekstu. Dlatego też liczbę tę należy starannie dobierać dla poszczególnych produktów.

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

Jeśli ta opcja jest włączona, usługa ART dexoptuje tylko aplikacje używane w ciągu ostatniej określonej liczby dni.

Ponadto, jeśli ilość miejsca na dysku jest prawie niska, podczas dexopt w tle usługa ART obniża wersję filtra kompilatora aplikacji, które nie były używane w ciągu ostatniej określonej liczby dni, aby zwolnić miejsce. Kompilator jest tego przyczyną inactive , a filtr kompilatora jest określany przez pm.dexopt.inactive . Próg miejsca uruchamiający tę funkcję to próg niskiego miejsca Menedżera przechowywania (konfigurowany za pomocą ustawień 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 dostarczonej przez BatchDexoptStartCallback dla bg-dexopt nigdy nie zostaną obniżone.

pm.dexopt.disable_bg_dexopt (domyślnie: fałsz)

To służy wyłącznie do testowania. Uniemożliwia usłudze ART planowanie zadania dexopt w tle.

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

Zalecana sekwencja poleceń zapobiegających uruchomieniu zadania dexopt w tle to:

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

Pierwsza linia uniemożliwia zaplanowanie zadania dexopt w tle, jeśli nie zostało ono jeszcze zaplanowane. Druga linia anuluje harmonogram zadania dexopt w tle, jeśli jest już zaplanowane, i natychmiast anuluje zadanie dexopt w tle, jeśli jest uruchomione.

Interfejsy API usług ART

Usługa ART udostępnia interfejsy API języka Java umożliwiające dostosowywanie. Interfejsy API są zdefiniowane w ArtManagerLocal . Zobacz dokument Javadoc w art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java aby zapoznać się z zastosowaniami ( źródło Androida 14 , niepublikowane źródło programistyczne ).

ArtManagerLocal jest singletonem przechowywanym przez LocalManagerRegistry . Funkcja pomocnicza com.android.server.pm.DexOptHelper#getArtManagerLocal pomaga w jej uzyskaniu.

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

Większość interfejsów API wymaga wystąpienia PackageManagerLocal.FilteredSnapshot , które przechowuje informacje o wszystkich aplikacjach. Można go uzyskać, wywołując PackageManagerLocal#withFilteredSnapshot , gdzie PackageManagerLocal jest także 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 przedstawiono kilka typowych przypadków użycia interfejsów API.

Uruchom dexopt dla aplikacji

Możesz uruchomić dexopt dla dowolnej aplikacji w dowolnym momencie, wywołując ArtManagerLocal#dexoptPackage .

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

Możesz także podać swój własny powód dexopt. Jeśli to zrobisz, klasa priorytetu i filtr kompilatora muszą być jawnie ustawione.

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

Anuluj dexopt

Jeśli operacja jest inicjowana przez wywołanie pakietu dexoptPackage , można przekazać sygnał anulowania, który umożliwia anulowanie operacji w pewnym momencie. 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 także anulować dexopt w tle inicjowany przez usługę ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Uzyskaj wyniki dexoptu

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

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

// Process the result here.
...

Usługa ART sama inicjuje również operacje dexopt w wielu scenariuszach, takich jak dexopt w tle. Aby odsłuchać wszystkie wyniki dexopt, niezależnie od tego, czy operacja jest inicjowana przez wywołanie dexoptPackage , czy 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 uwzględnić tylko aktualizacje. Jeśli chcesz słuchać tylko pakietów aktualizowanych przez dexopt, ustaw tę opcję na 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 zwrotne blokowało dexopt, użyj asynchronicznego modułu wykonującego.

Możesz dodać wiele wywołań zwrotnych, a usługa ART wykona je wszystkie po kolei. Wszystkie wywołania zwrotne pozostaną aktywne dla wszystkich przyszłych połączeń, chyba że je usuniesz.

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

Dostosuj listę pakietów i parametry dexopt

Usługa ART sama inicjuje operacje dexopt podczas rozruchu i dexopt w tle. Aby dostosować listę pakietów lub parametry dexopt dla 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ć pozycje do listy pakietów, usuwać z niej pozycje, sortować je, a nawet korzystać z zupełnie innej listy.

Twoje wywołanie zwrotne musi ignorować nieznane przyczyny, ponieważ w przyszłości może zostać dodanych więcej powodów.

Można ustawić maksymalnie jedną BatchDexoptStartCallback . Wywołanie zwrotne pozostanie aktywne dla wszystkich przyszłych połączeń, chyba że je usuniesz.

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

getArtManagerLocal().clearBatchDexoptStartCallback();

Dostosuj parametry zadania dexopt w tle

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

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

Można ustawić maksymalnie jedną ScheduleBackgroundDexoptJobCallback . Wywołanie zwrotne pozostanie aktywne dla wszystkich przyszłych połączeń, chyba że je usuniesz.

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

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Tymczasowo wyłącz dexopt

Każda operacja dexopt inicjowana przez usługę ART wyzwala BatchDexoptStartCallback . Możesz nadal anulować operacje, aby skutecznie wyłączyć dexopt.

Jeśli anulowana operacja jest wykonywana w tle, jest ona zgodna z domyślnymi zasadami ponawiania (30 sekund, wykładniczo, z ograniczeniem 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 jedną BatchDexoptStartCallback . Jeśli chcesz także użyć BatchDexoptStartCallback do dostosowania 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 poprzez wywołanie dexoptPackage . Dlatego nie wyzwala BatchDexoptStartCallback . Aby wyłączyć dexopt podczas instalacji aplikacji, uniemożliwij menedżerowi pakietów wywoływanie dexoptPackage .

Zastąp filtr kompilatora dla niektórych pakietów (Android 15 (eksperymentalny AOSP)+)

Możesz zastąpić filtr kompilatora dla niektórych pakietów, rejestrując wywołanie zwrotne za pomocą setAdjustCompilerFilterCallback . Wywołanie zwrotne jest wywoływane za każdym razem, gdy pakiet ma zostać dexopt, niezależnie od tego, czy dexopt jest inicjowany przez usługę ART podczas rozruchu i dexopt w tle, czy też przez wywołanie API dexoptPackage .

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

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

Można ustawić tylko jedną AdjustCompilerFilterCallback . Jeśli chcesz użyć AdjustCompilerFilterCallback do zastąpienia filtru kompilatora dla wielu pakietów, musisz połączyć kod w jedno wywołanie zwrotne. Wywołanie zwrotne pozostanie aktywne dla wszystkich przyszłych połączeń, chyba że je usuniesz.

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

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Inne dostosowania

Usługa ART obsługuje również inne dostosowania.

Ustaw próg termiczny dla tła dexopt

Kontrola termiczna zadania dexopt w tle jest wykonywana przez Harmonogram zadań. Zadanie zostaje anulowane natychmiast, gdy temperatura osiągnie THERMAL_STATUS_MODERATE . Próg THERMAL_STATUS_MODERATE można regulować.

Sprawdź, czy działa dexopt w tle

Zadanie dexopt w tle jest zarządzane przez Harmonogram zadań, a jego identyfikator zadania to 27873780 . Aby ustalić, czy zadanie jest uruchomione, użyj interfejsów API Harmonogramu zadań.

// 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żyć profilu do prowadzenia dexopt, umieść plik .prof lub plik .dm obok pliku APK.

Plik .prof musi być plikiem profilu w formacie binarnym, a nazwa pliku musi być nazwą pliku APK + .prof . Na przykład,

base.apk.prof

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

base.dm

Aby sprawdzić, czy profil jest używany przez dexopt, uruchom dexopt z speed-profile i sprawdź wynik.

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

Pierwsza linia czyści wszystkie profile utworzone przez środowisko wykonawcze (tj. te w /data/misc/profiles ), jeśli istnieją, aby upewnić się, że profil obok pliku APK jest jedynym profilem, z którego może korzystać usługa ART. Druga linia uruchamia dexopt z speed-profile i przekazuje -v , aby wydrukować pełny 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 powody, dla których ART Service nie korzysta z profilu, to:

  • Profil ma błędną nazwę pliku lub nie ma go obok pliku APK.
  • Profil ma nieprawidłowy format.
  • Profil nie jest zgodny z plikiem APK. (Sumy kontrolne w profilu nie odpowiadają sumom kontrolnym plików .dex w pliku APK.)