Prima di iniziare, consulta una panoramica generale del servizio ART.
A partire da Android 14, la compilazione AOT on-device per le app (ovvero dexopt) viene gestita dal servizio ART. ART Service fa parte del modulo ART e puoi personalizzarlo tramite proprietà di sistema e API.
Proprietà di sistema
Il servizio ART supporta tutte le opzioni dex2oat pertinenti.
Inoltre, il servizio ART supporta le seguenti proprietà di sistema:
pm.dexopt.<reason>
Si tratta di un insieme di proprietà di sistema che determinano i filtri del compilatore predefiniti per tutti i motivi di compilazione predefiniti descritti in Scenari di dexopt.
Per saperne di più, vedi Filtri del compilatore.
I valori predefiniti standard sono:
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 (predefinito: speed)
Questo è il filtro del compilatore di riserva per le app utilizzate da altre app.
In linea di principio, ART Service esegue la compilazione guidata dal profilo (speed-profile
) per
tutte le app, se possibile, in genere durante l'ottimizzazione dexopt in background. Tuttavia, esistono
alcune app utilizzate da altre app (tramite <uses-library>
o caricate
dinamicamente utilizzando Context#createPackageContext
con
CONTEXT_INCLUDE_CODE
). Queste app non possono utilizzare profili
locali per motivi di privacy.
Per un'app di questo tipo, se viene richiesta la compilazione guidata dal profilo, il servizio ART tenta innanzitutto di utilizzare un profilo cloud. Se non esiste un profilo cloud, il servizio ART
utilizza il filtro del compilatore specificato da pm.dexopt.shared
.
Se la compilazione richiesta non è guidata dal profilo, questa proprietà non ha alcun effetto.
pm.dexopt.<reason>.concurrency (impostazione predefinita: 1)
Questo è il numero di chiamate dex2oat per determinati motivi di compilazione predefiniti (first-boot
, boot-after-ota
, boot-after-mainline-update
e bg-dexopt
).
Tieni presente che l'effetto di questa opzione viene combinato con
le opzioni di utilizzo delle risorse dex2oat (dalvik.vm.*dex2oat-threads
,
dalvik.vm.*dex2oat-cpu-set
e i profili delle attività):
dalvik.vm.*dex2oat-threads
controlla il numero di thread per ogni chiamata dex2oat, mentrepm.dexopt.<reason>.concurrency
controlla il numero di chiamate dex2oat. ovvero il numero massimo di thread simultanei è il prodotto delle due proprietà di sistema.dalvik.vm.*dex2oat-cpu-set
e i profili delle attività vincolano sempre l'utilizzo dei core della CPU, indipendentemente dal numero massimo di thread simultanei (illustrato sopra).
Una singola chiamata dex2oat potrebbe non utilizzare completamente tutti i core della CPU, indipendentemente
da dalvik.vm.*dex2oat-threads
. Pertanto, l'aumento del numero di invocazioni di dex2oat (pm.dexopt.<reason>.concurrency
) può utilizzare meglio i core della CPU per
accelerare l'avanzamento complessivo di dexopt. Ciò è particolarmente utile durante
l'avvio.
Tuttavia, un numero eccessivo di chiamate dex2oat potrebbe causare l'esaurimento della memoria del dispositivo, anche se questo problema può essere mitigato impostando dalvik.vm.dex2oat-swap
su true
per consentire l'utilizzo di un file di swap. Troppe invocazioni potrebbero anche causare
cambi di contesto non necessari. Pertanto, questo numero deve essere regolato con attenzione
in base al prodotto.
pm.dexopt.downgrade_after_inactive_days (impostazione predefinita: non impostato)
Se questa opzione è impostata, ART Service esegue l'ottimizzazione dex solo delle app utilizzate nell'ultimo numero di giorni specificato.
Inoltre, se lo spazio di archiviazione è quasi esaurito, durante l'ottimizzazione dexopt in background, il servizio ART
esegue il downgrade del filtro del compilatore delle app che non vengono utilizzate entro l'ultimo numero di giorni specificato
per liberare spazio. Il motivo del compilatore è inactive
,
e il filtro del compilatore è determinato da pm.dexopt.inactive
. La soglia di spazio
per attivare questa funzionalità è la soglia di spazio ridotto di Storage Manager
(configurabile tramite le impostazioni globali sys_storage_threshold_percentage
e
sys_storage_threshold_max_bytes
, valore predefinito: 500 MB) più 500 MB.
Se personalizzi l'elenco dei pacchetti tramite
ArtManagerLocal#setBatchDexoptStartCallback
, i pacchetti nell'elenco fornito
da BatchDexoptStartCallback
per bg-dexopt
non vengono mai declassati.
pm.dexopt.disable_bg_dexopt (impostazione predefinita: false)
Solo per i test. Impedisce al servizio ART di pianificare il job dexopt in background.
Se il job dexopt in background è già pianificato ma non è ancora stato eseguito, questa opzione non ha alcun effetto. ovvero il job verrà comunque eseguito.
Una sequenza consigliata di comandi per impedire l'esecuzione del job dexopt in background è:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
La prima riga impedisce la pianificazione del job dexopt in background, se non è ancora pianificato. La seconda riga annulla la pianificazione del job dexopt in background, se è già pianificato, e lo annulla immediatamente, se è in esecuzione.
API di servizio ART
Il servizio ART espone API Java per la personalizzazione. Le API sono definite in
ArtManagerLocal
. Consulta la documentazione Javadoc in
art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
per
gli utilizzi (origine Android 14, origine di sviluppo non rilasciata).
ArtManagerLocal
è un singleton detenuto da LocalManagerRegistry
. Una funzione
helper com.android.server.pm.DexOptHelper#getArtManagerLocal
ti aiuta
a ottenerlo.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
La maggior parte delle API richiede un'istanza di PackageManagerLocal.FilteredSnapshot
,
che contiene le informazioni di tutte le app. Puoi ottenerlo chiamando
PackageManagerLocal#withFilteredSnapshot
, dove PackageManagerLocal
è anche
un singleton detenuto da LocalManagerRegistry
e può essere ottenuto da
com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal
.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
Di seguito sono riportati alcuni casi d'uso tipici delle API.
Attivare dexopt per un'app
Puoi attivare dexopt per qualsiasi app in qualsiasi momento chiamando
ArtManagerLocal#dexoptPackage
.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
Puoi anche passare il tuo motivo di dexopt. In questo caso, la classe di priorità e il filtro del compilatore devono essere impostati in modo esplicito.
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());
}
Annulla dexopt
Se un'operazione viene avviata da una chiamata dexoptPackage
, puoi passare un segnale di annullamento, che ti consente di annullare l'operazione a un certo punto. Questa operazione può
essere utile quando esegui dexopt in modo asincrono.
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();
Puoi anche annullare l'ottimizzazione dexopt in background, avviata dal servizio ART.
getArtManagerLocal().cancelBackgroundDexoptJob();
Ottenere i risultati di dexopt
Se un'operazione viene avviata da una chiamata dexoptPackage
, puoi ottenere il risultato
dal valore restituito.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
Il servizio ART avvia anche le operazioni dexopt in molti scenari, ad esempio
dexopt in background. Per ascoltare tutti i risultati di dexopt, indipendentemente dal fatto che l'operazione venga avviata da una chiamata dexoptPackage
o dal servizio ART, utilizza ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Il primo argomento determina se includere solo gli aggiornamenti nel risultato. Se vuoi ascoltare solo i pacchetti aggiornati da dexopt, impostalo su true.
Il secondo argomento è l'esecutore del callback. Per eseguire il callback sullo stesso thread che esegue dexopt, utilizza Runnable::run
. Se non vuoi che il
callback blocchi dexopt, utilizza un executor asincrono.
Puoi aggiungere più callback e il servizio ART li eseguirà in sequenza. Tutte le richiamate rimarranno attive per tutte le chiamate future, a meno che non le rimuovi.
Se vuoi rimuovere un callback, mantieni il riferimento del callback quando lo
aggiungi e utilizza 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);
Personalizzare l'elenco dei pacchetti e i parametri dexopt
Il servizio ART avvia le operazioni dexopt durante l'avvio e dexopt in background. Per personalizzare l'elenco dei pacchetti o i parametri dexopt per queste operazioni,
utilizza 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.
}
});
Puoi aggiungere elementi all'elenco dei pacchetti, rimuoverli, ordinarli o persino utilizzare un elenco completamente diverso.
Il callback deve ignorare i motivi sconosciuti perché in futuro potrebbero essere aggiunti altri motivi.
Puoi impostare al massimo un BatchDexoptStartCallback
. Il richiamo rimarrà
attivo per tutte le chiamate future, a meno che non lo cancelli.
Se vuoi cancellare il callback, utilizza
ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Personalizzare i parametri del job dexopt in background
Per impostazione predefinita, il job dexopt in background viene eseguito una volta al giorno quando il dispositivo è inattivo
e in carica. Questa impostazione può essere modificata utilizzando
ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Puoi impostare al massimo un ScheduleBackgroundDexoptJobCallback
. Il richiamo
rimarrà attivo per tutte le chiamate future, a meno che tu non lo cancelli.
Se vuoi cancellare il callback, utilizza
ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
Disattivare temporaneamente dexopt
Qualsiasi operazione dexopt avviata da ART Service attiva un
BatchDexoptStartCallback
. Puoi continuare ad annullare le operazioni per
disattivare dexopt.
Se l'operazione che annulli è dexopt in background, segue il criterio di nuovo tentativo predefinito (30 secondi, esponenziale, con un limite di 5 ore).
// 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);
Puoi avere al massimo un BatchDexoptStartCallback
. Se vuoi utilizzare anche
BatchDexoptStartCallback
per personalizzare l'elenco dei pacchetti o i parametri dexopt,
devi combinare il codice in un unico callback.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
L'operazione dexopt eseguita durante l'installazione dell'app non viene avviata da ART
Service. Viene invece avviato dal gestore dei pacchetti tramite una chiamata
dexoptPackage
. Pertanto, non attiva
BatchDexoptStartCallback
. Per disattivare dexopt durante l'installazione dell'app, impedisci a
package manager di chiamare dexoptPackage
.
Ignorare il filtro del compilatore per determinati pacchetti (Android 15 e versioni successive)
Puoi eseguire l'override del filtro del compilatore per determinati pacchetti registrando un
callback tramite setAdjustCompilerFilterCallback
. Il callback viene chiamato
ogni volta che un pacchetto sta per essere sottoposto a dexopt, indipendentemente dal fatto che l'operazione sia avviata da
ART Service durante l'avvio e il dexopt in background o da una chiamata API dexoptPackage
.
Se un pacchetto non richiede modifiche, il callback deve restituire
originalCompilerFilter
.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
Puoi impostare un solo AdjustCompilerFilterCallback
. Se vuoi utilizzare
AdjustCompilerFilterCallback
per ignorare il filtro del compilatore per più
pacchetti, devi combinare il codice in un unico callback. Il richiamo rimane
attivo per tutte le chiamate future, a meno che non lo cancelli.
Se vuoi cancellare il callback, utilizza
ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Altre personalizzazioni
Il servizio ART supporta anche altre personalizzazioni.
Imposta la soglia termica per dexopt in background
Il controllo termico del job dexopt in background viene eseguito da Job Scheduler.
Il job viene annullato immediatamente quando la temperatura raggiunge
THERMAL_STATUS_MODERATE
. La soglia di
THERMAL_STATUS_MODERATE
è regolabile.
Determinare se dexopt in background è in esecuzione
Il job dexopt in background è gestito da Job Scheduler e il suo ID job è
27873780
. Per determinare se il job è in esecuzione, utilizza le API Job Scheduler.
// 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.
...
}
Fornisci un profilo per dexopt
Per utilizzare un profilo per guidare dexopt, inserisci un file .prof
o un file .dm
accanto all'APK.
Il file .prof
deve essere un file di profilo in formato binario e il nome file deve essere
il nome file dell'APK + .prof
. Ad esempio,
base.apk.prof
Il nome del file .dm
deve essere il nome del file APK con l'estensione sostituita da .dm
. Ad esempio,
base.dm
Per verificare che il profilo venga utilizzato per dexopt, esegui dexopt con
speed-profile
e controlla il risultato.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
La prima riga cancella tutti i profili prodotti dal runtime (ovvero quelli in
/data/misc/profiles
), se presenti, per assicurarsi che il profilo accanto all'APK sia
l'unico profilo che ART Service può utilizzare. La seconda riga esegue dexopt
con speed-profile
e passa -v
per stampare il risultato dettagliato.
Se il profilo è in uso, nel risultato viene visualizzato actualCompilerFilter=speed-profile
. In caso contrario, vedrai actualCompilerFilter=verify
. Ad esempio,
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}
I motivi tipici per cui il servizio ART non utilizza il profilo includono:
- Il profilo ha un nome file errato o non si trova accanto all'APK.
- Il profilo non è nel formato corretto.
- Il profilo non corrisponde all'APK. (I checksum nel profilo non
corrispondono ai checksum dei file
.dex
nell'APK.)