Zanim zaczniesz, zapoznaj się z ogólnym opisem usługi ART.
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żesz ją dostosowywać za pomocą właściwości systemu i interfejsów API.
Właściwości systemowe
Usługa ART obsługuje wszystkie odpowiednie opcje dex2oat.
Usługa ART obsługuje też te właściwości systemu:
pm.dexopt.<reason>
Jest to zestaw właściwości systemu, które określają domyślne filtry kompilatora dla wszystkich predefiniowanych przyczyn kompilacji opisanych w scenariuszach optymalizacji Dex.
Więcej informacji znajdziesz w artykule 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: speed)
Jest to filtr kompilatora rezerwowego dla aplikacji używanych przez inne aplikacje.
Usługa ART w miarę możliwości przeprowadza kompilację z optymalizacją na podstawie profilu (speed-profile
) w przypadku wszystkich aplikacji, zwykle podczas optymalizacji dex w tle. Istnieją jednak aplikacje, które są używane przez inne aplikacje (za pomocą <uses-library>
lub ładowane dynamicznie przy użyciu Context#createPackageContext
z CONTEXT_INCLUDE_CODE
). Ze względu na ochronę prywatności nie mogą one używać profili lokalnych.
W przypadku takiej aplikacji, jeśli zażądano kompilacji opartej na profilu, usługa ART najpierw próbuje użyć profilu w chmurze. Jeśli profil w chmurze nie istnieje, usługa ART Service używa filtra kompilatora określonego przez pm.dexopt.shared
.
Jeśli żądana kompilacja nie jest oparta na profilu, ta właściwość nie ma wpływu.
pm.dexopt.<reason>.concurrency (domyślnie: 1)
Jest to liczba wywołań dex2oat z określonych, zdefiniowanych wcześniej powodów kompilacji (first-boot
, boot-after-ota
, boot-after-mainline-update
i bg-dexopt
).
Pamiętaj, że efekt tej opcji jest łączony z opcjami wykorzystania zasobów dex2oat (dalvik.vm.*dex2oat-threads
, dalvik.vm.*dex2oat-cpu-set
i profile zadań):
dalvik.vm.*dex2oat-threads
określa liczbę wątków dla każdego wywołania dex2oat, apm.dexopt.<reason>.concurrency
określa liczbę wywołań dex2oat. Oznacza to, że maksymalna liczba równoległych wątków jest iloczynem tych dwóch właściwości systemu.dalvik.vm.*dex2oat-cpu-set
, a profile zadań zawsze ograniczają wykorzystanie rdzenia procesora niezależnie od maksymalnej liczby równoczesnych wątków (omówionej powyżej).
Pojedyncze wywołanie dex2oat może nie w pełni wykorzystywać wszystkich rdzeni procesora, niezależnie od wartości 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 wiele wywołań dex2oat może spowodować wyczerpanie pamięci urządzenia, chociaż można temu zapobiec, 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 tę liczbę należy dokładnie dostosowywać do poszczególnych produktów.
pm.dexopt.downgrade_after_inactive_days (domyślnie: nie ustawiono)
Jeśli ta opcja jest ustawiona, usługa ART optymalizuje tylko aplikacje używane w ciągu ostatnich kilku dni.
Dodatkowo, jeśli ilość miejsca na dane jest bliska wyczerpania, podczas optymalizacji DEX w tle usługa ART obniża filtr kompilatora aplikacji, które nie były używane w ciągu ostatnich kilku dni, aby zwolnić miejsce. Powód kompilatora to inactive
, a filtr kompilatora jest określany przez pm.dexopt.inactive
. Próg przestrzeni dyskowej, który uruchamia tę funkcję, to próg małej ilości miejsca na dysku w Menedżerze pamięci (konfigurowany 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 dostarczonej przez BatchDexoptStartCallback
dla bg-dexopt
nigdy nie zostaną obniżone.
pm.dexopt.disable_bg_dexopt (domyślnie: false)
Służy tylko do testowania. Uniemożliwia to usłudze ART zaplanowanie zadania dexopt w tle.
Jeśli zadanie dexopt w tle jest już zaplanowane, ale jeszcze nie zostało uruchomione, ta opcja nie ma wpływu. Oznacza to, że zadanie nadal będzie wykonywane.
Zalecana sekwencja poleceń, która zapobiega uruchomieniu zadania dexopt w tle:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Pierwsza linia zapobiega zaplanowaniu zadania dexopt w tle, jeśli nie zostało ono jeszcze zaplanowane. Druga linia anuluje zaplanowane zadanie dexopt w tle, jeśli jest ono już zaplanowane, i natychmiast je anuluje, jeśli jest ono uruchomione.
Interfejsy API usługi ART
Usługa ART udostępnia interfejsy API w Javie, które można dostosowywać. Interfejsy API są zdefiniowane w ArtManagerLocal
. Zobacz dokumentację Javadoc w art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
, aby poznać zastosowania (źródło Androida 14, nieopublikowane źródło deweloperskie).
ArtManagerLocal
to pojedynczy element należący do LocalManagerRegistry
. Funkcja pomocnicza com.android.server.pm.DexOptHelper#getArtManagerLocal
ułatwia Ci uzyskanie tego kodu.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
Większość interfejsów API wymaga instancji PackageManagerLocal.FilteredSnapshot
, która zawiera informacje o wszystkich aplikacjach. Możesz go uzyskać, wywołując PackageManagerLocal#withFilteredSnapshot
, gdzie PackageManagerLocal
jest też 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 znajdziesz typowe zastosowania interfejsów API.
Wywoływanie optymalizacji dex dla 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ż przekazać własny powód optymalizacji dex. W takim przypadku musisz jawnie ustawić klasę priorytetu i filtr kompilatora.
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 optymalizacji DEX
Jeśli operacja jest inicjowana przez wywołanie dexoptPackage
, możesz przekazać sygnał anulowania, który umożliwia anulowanie operacji w dowolnym momencie. Może to być przydatne, gdy dexopt jest uruchamiany 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ć optymalizację dex w tle, która jest inicjowana przez usługę ART.
getArtManagerLocal().cancelBackgroundDexoptJob();
Pobieranie wyników dexopt
Jeśli operacja zostanie zainicjowana przez wywołanie 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 sama inicjuje operacje dexopt w wielu scenariuszach, np. w przypadku 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 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ę wartość na true.
Drugi argument to wykonawca wywołania zwrotnego. Aby wykonać wywołanie zwrotne w tym samym wątku, w którym wykonywane jest dexopt, użyj Runnable::run
. Jeśli nie chcesz, aby wywołanie zwrotne blokowało dexopt, użyj wykonawcy asynchronicznego.
Możesz dodać wiele wywołań zwrotnych, a usługa ART wykona je wszystkie po kolei. Wszystkie wywołania zwrotne pozostaną aktywne w przypadku wszystkich przyszłych połączeń, chyba że je usuniesz.
Jeśli chcesz usunąć wywołanie zwrotne, zachowaj odniesienie 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 sama inicjuje operacje dexopt podczas uruchamiania i 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ć elementy do listy pakietów, usuwać je z niej, sortować ją, a nawet używać zupełnie innej listy.
Funkcja zwrotna musi ignorować nieznane przyczyny, ponieważ w przyszłości może zostać dodanych więcej powodów.
Możesz ustawić maksymalnie 1 BatchDexoptStartCallback
. Funkcja oddzwaniania pozostanie aktywna w przypadku wszystkich przyszłych połączeń, dopóki jej nie wyłączysz.
Jeśli chcesz wyczyścić wywołanie zwrotne, użyj ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Dostosowywanie parametrów zadania dexopt w tle
Domyślnie zadanie dexopt w tle jest uruchamiane raz dziennie, gdy urządzenie jest nieużywane i się ładuje. Możesz to zmienić, korzystając z ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Możesz ustawić maksymalnie 1 ScheduleBackgroundDexoptJobCallback
. Funkcja oddzwaniania pozostanie aktywna w przypadku wszystkich przyszłych połączeń, dopóki jej nie wyłączysz.
Jeśli chcesz wyczyścić wywołanie zwrotne, użyj ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
Tymczasowe wyłączenie dexopt
Zadanie dexopt w tle jest uruchamiane tylko wtedy, gdy urządzenie jest bezczynne, i jest natychmiast anulowane, jeśli urządzenie przestanie być bezczynne. W poprzednich wersjach Androida proces dexopt był obsługiwany przez menedżera pakietów, który miał zadanie wykonywane po uruchomieniu systemu. Czasami konkurowało ono o zasoby z aplikacją działającą na pierwszym planie, co powodowało problemy z wydajnością. Usługa ART nie ma takiego zadania.Każda operacja dexopt zainicjowana przez usługę ART wywołuje BatchDexoptStartCallback
. Możesz anulować operacje, aby skutecznie wyłączyć dexopt.
Jeśli anulowana operacja to optymalizacja DEX w tle, obowiązuje domyślna zasada ponawiania (30 sekund, wykładniczo, maksymalnie 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żyć elementu
BatchDexoptStartCallback
do dostosowania listy pakietów lub parametrów dexopt,
musisz połączyć kod w jednym wywołaniu zwrotnym.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
Operacja dexopt wykonywana podczas instalacji aplikacji nie jest inicjowana przez ART Service. Jest ona inicjowana przez menedżera pakietów za pomocą wywołania dexoptPackage
. Dlatego nie wywołuje on
BatchDexoptStartCallback
. Aby wyłączyć dexopt podczas instalowania aplikacji, uniemożliw menedżerowi pakietów wywoływanie funkcji dexoptPackage
.
Zastępowanie filtra kompilatora w przypadku wybranych pakietów (Android 15 lub nowszy)
Możesz zastąpić filtr kompilatora dla określonych pakietów, rejestrując wywołanie zwrotne za pomocą funkcji setAdjustCompilerFilterCallback
. Funkcja zwrotna jest wywoływana za każdym razem, gdy pakiet ma zostać zoptymalizowany za pomocą narzędzia dexopt, niezależnie od tego, czy optymalizacja została zainicjowana przez usługę ART podczas uruchamiania i optymalizacji w tle, czy przez wywołanie interfejsu API dexoptPackage
.
Jeśli pakiet nie wymaga dostosowania, 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ć
AdjustCompilerFilterCallback
, aby zastąpić filtr kompilatora w przypadku wielu pakietów, musisz połączyć kod w jednym wywołaniu zwrotnym. Funkcja oddzwaniania pozostanie aktywna w przypadku wszystkich przyszłych połączeń, dopóki jej nie wyłączysz.
Jeśli chcesz wyczyścić wywołanie zwrotne, użyj ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Inne dostosowania
Usługa ART obsługuje też inne dostosowania.
Ustawianie progu termicznego dla optymalizacji dex w tle
Kontrola termiczna zadania dexopt w tle jest wykonywana przez harmonogram zadań.
Zadanie zostanie natychmiast anulowane, gdy temperatura osiągnie THERMAL_STATUS_MODERATE
. Próg THERMAL_STATUS_MODERATE
można dostosować.
Sprawdzanie, czy optymalizacja dex w tle jest uruchomiona
Zadaniem dexopt w tle zarządza Harmonogram zadań, a jego identyfikator to 27873780
. Aby sprawdzić, czy zadanie jest uruchomione, 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.
...
}
pm.dexopt.downgrade_after_inactive_days
jest włączone, a miejsce na dane jest prawie wyczerpane, Usługi ART wywołują funkcję BatchDexoptStartCallback
raz, a funkcję DexoptDoneCallback
dwa razy (raz w celu obniżenia wersji i raz w celu optymalizacji dex).// 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 sterowania optymalizacją dexopt, umieść plik .prof
lub .dm
obok pliku APK.
Plik .prof
musi być plikiem profilu w formacie binarnym, a jego nazwa musi być nazwą pliku APK + .prof
. Na przykład
base.apk.prof
Nazwa pliku .dm
musi być taka sama jak nazwa pliku APK, ale rozszerzenie musi być zastąpione ciągiem .dm
. Na przykład
base.dm
Aby sprawdzić, czy profil jest używany do optymalizacji dexopt, uruchom dexopt z parametremspeed-profile
i sprawdź wynik.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
Pierwsza linia usuwa wszystkie profile wygenerowane przez środowisko wykonawcze (czyli te w /data/misc/profiles
), aby mieć pewność, że profil obok pliku APK jest jedynym profilem, którego może używać usługa ART. Druga linia uruchamia dexopt z parametrem speed-profile
i przekazuje -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 korzysta z profilu, to:
- 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 APK).