Configurazione del servizio ART

Prima di iniziare, consulta una panoramica di alto livello del servizio ART .

A partire da Android 14, la compilazione AOT sul dispositivo per le app (nota anche come dexopt) è gestita da ART Service. ART Service fa parte del modulo ART ed è possibile personalizzarlo tramite le proprietà del sistema e le API.

Proprietà di sistema

ART Service supporta tutte le opzioni dex2oat rilevanti.

Inoltre, ART Service supporta le seguenti proprietà di sistema:

pm.dexopt.<motivo>

Si tratta di un insieme di proprietà di sistema che determinano i filtri predefiniti del compilatore per tutti i motivi di compilazione predefiniti descritti in Scenari Dexopt .

Per ulteriori informazioni, consulta 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: velocità)

Questo è il filtro del compilatore di fallback 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 quando possibile, in genere durante il dexopt in background. Tuttavia, ci sono alcune app utilizzate da altre app (tramite <uses-library> o caricate dinamicamente utilizzando Context#createPackageContext con CONTEXT_INCLUDE_CODE ). Tali app non possono utilizzare profili locali per motivi di privacy.

Per tale app, se è richiesta la compilazione guidata dal profilo, ART Service tenta innanzitutto di utilizzare un profilo cloud. Se un profilo cloud non esiste, ART Service ricorre all'utilizzo del filtro del compilatore specificato da pm.dexopt.shared .

Se la compilazione richiesta non è guidata dal profilo questa proprietà non ha alcun effetto.

pm.dexopt.<motivo>.concurrency (predefinito: 1)

Questo è il numero di invocazioni di 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 è 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 invocazione di dex2oat, mentre pm.dexopt.<reason>.concurrency controlla il numero di invocazioni di dex2oat. Cioè, il numero massimo di thread simultanei è il prodotto delle due proprietà di sistema.
  • dalvik.vm.*dex2oat-cpu-set e i profili delle attività limitano sempre l'utilizzo del core della CPU, indipendentemente dal numero massimo di thread simultanei (discusso sopra).

Una singola invocazione di dex2oat potrebbe non utilizzare completamente tutti i core della CPU, indipendentemente da dalvik.vm.*dex2oat-threads . Pertanto, aumentando il numero di invocazioni dex2oat ( pm.dexopt.<reason>.concurrency ) è possibile utilizzare meglio i core della CPU, per accelerare il progresso complessivo di dexopt. Ciò è particolarmente utile durante l'avvio.

Tuttavia, avere troppe invocazioni di dex2oat potrebbe causare l'esaurimento della memoria del dispositivo, anche se questo può essere mitigato impostando dalvik.vm.dex2oat-swap su true per consentire l'utilizzo di un file di scambio. Troppe invocazioni potrebbero anche causare un cambio di contesto non necessario. Pertanto, questo numero dovrebbe essere attentamente calibrato prodotto per prodotto.

pm.dexopt.downgrade_after_inactive_days (impostazione predefinita: non impostato)

Se questa opzione è impostata, ART Service esegue il dexoption solo delle app utilizzate nell'ultimo numero di giorni specificato.

Inoltre, se lo spazio di archiviazione è quasi esaurito, durante il dexopt in background, ART Service esegue il downgrade del filtro del compilatore delle app che non vengono utilizzate nell'ultimo determinato numero di giorni, 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 funzione è la soglia di spazio basso di Gestione archiviazione (configurabile tramite le impostazioni globali sys_storage_threshold_percentage e sys_storage_threshold_max_bytes , impostazione predefinita: 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 verranno mai sottoposti a downgrade.

pm.dexopt.disable_bg_dexopt (predefinito: falso)

Questo è solo per testare. Impedisce ad ART Service di pianificare il processo di dexopt in background.

Se il processo dexopt in background è già pianificato ma non è ancora stato eseguito, questa opzione non ha alcun effetto. Cioè, il lavoro continuerà a essere eseguito.

Una sequenza di comandi consigliata per impedire l'esecuzione del processo dexopt in background è:

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

La prima riga impedisce la pianificazione del processo dexopt in background, se non è ancora pianificato. La seconda riga annulla la pianificazione del processo dexopt in background, se è già pianificato, e lo annulla immediatamente, se è in esecuzione.

API del servizio ART

ART Service espone le API Java per la personalizzazione. Le API sono definite in ArtManagerLocal . Consulta il Javadoc in art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java per gli utilizzi ( fonte Android 14 , fonte di sviluppo non rilasciata ).

ArtManagerLocal è un singleton mantenuto da LocalManagerRegistry . Una funzione di supporto 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 gestito 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.

Attiva il 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 dexopt. In tal caso, la classe di priorità e il filtro del compilatore devono essere impostati esplicitamente.

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

È inoltre possibile annullare il dexopt in background, avviato da ART Service.

getArtManagerLocal().cancelBackgroundDexoptJob();

Ottieni risultati 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.
...

ART Service avvia inoltre le operazioni di dexopt stesso in molti scenari, come il dexopt in background. Per ascoltare tutti i risultati dexopt, indipendentemente dal fatto che l'operazione venga avviata da una chiamata dexoptPackage o da ART Service, utilizzare 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 la richiamata sullo stesso thread che esegue dexopt, utilizzare Runnable::run . Se non vuoi che la richiamata blocchi dexopt, usa un esecutore asincrono.

È possibile aggiungere più callback e ART Service li eseguirà tutti in sequenza. Tutte le richiamate rimarranno attive per tutte le chiamate future a meno che non le rimuovi.

Se desideri rimuovere una richiamata, mantieni il riferimento della richiamata quando la 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);

Personalizza l'elenco dei pacchetti e i parametri dexopt

ART Service avvia autonomamente le operazioni di dexopt durante l'avvio e il dexopt in background. Per personalizzare l'elenco dei pacchetti o i parametri dexopt per tali operazioni, utilizzare 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, rimuovere elementi da esso, ordinarli o persino utilizzare un elenco completamente diverso.

La richiamata deve ignorare i motivi sconosciuti perché in futuro potrebbero essere aggiunti altri motivi.

È possibile impostare al massimo un BatchDexoptStartCallback . La richiamata rimarrà attiva per tutte le chiamate future a meno che non la cancelli.

Se desideri cancellare la richiamata, utilizza ArtManagerLocal#clearBatchDexoptStartCallback .

getArtManagerLocal().clearBatchDexoptStartCallback();

Personalizza i parametri del lavoro dexopt in background

Per impostazione predefinita, il processo dexopt in background viene eseguito una volta al giorno quando il dispositivo è inattivo e in carica. Questo può essere modificato utilizzando ArtManagerLocal#setScheduleBackgroundDexoptJobCallback .

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

È possibile impostare al massimo uno ScheduleBackgroundDexoptJobCallback . La richiamata rimarrà attiva per tutte le chiamate future a meno che non la cancelli.

Se desideri cancellare la richiamata, utilizza ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Disabilita temporaneamente dexopt

Qualsiasi operazione dexopt avviata da ART Service attiva un BatchDexoptStartCallback . Puoi continuare ad annullare le operazioni per disabilitare efficacemente dexopt.

Se l'operazione annullata è un dexopt in background, segue la policy di ripetizione predefinita (30 secondi, esponenziale, limitato a 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 desideri utilizzare BatchDexoptStartCallback anche 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 pacchetti tramite una chiamata dexoptPackage . Pertanto, non attiva BatchDexoptStartCallback . Per disabilitare dexopt all'installazione dell'app, impedire al gestore pacchetti di chiamare dexoptPackage .

Sostituisci il filtro del compilatore per determinati pacchetti (Android 15 (AOSP sperimentale)+)

È possibile sovrascrivere il filtro del compilatore per determinati pacchetti registrando un callback tramite setAdjustCompilerFilterCallback . Il callback viene chiamato ogni volta che un pacchetto verrà dexopt, indipendentemente dal fatto che il dexopt venga avviato da ART Service durante l'avvio e il dexopt in background o da una chiamata API dexoptPackage .

Se un pacchetto non necessita di modifiche, il callback deve restituire originalCompilerFilter .

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

È possibile impostare solo un AdjustCompilerFilterCallback . Se desideri utilizzare AdjustCompilerFilterCallback per sovrascrivere il filtro del compilatore per più pacchetti, devi combinare il codice in un unico callback. La richiamata rimane attiva per tutte le chiamate future a meno che non la si cancelli.

Se desideri cancellare la richiamata, utilizza ArtManagerLocal#clearAdjustCompilerFilterCallback .

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Altre personalizzazioni

ART Service supporta anche alcune altre personalizzazioni.

Imposta la soglia termica per il dexopt dello sfondo

Il controllo termico del lavoro dexopt in background viene eseguito da Job Scheduler. Il lavoro viene annullato immediatamente quando la temperatura raggiunge THERMAL_STATUS_MODERATE . La soglia di THERMAL_STATUS_MODERATE è regolabile.

Determina se il dexopt in background è in esecuzione

Il lavoro dexopt in background è gestito da Job Scheduler e il suo ID lavoro è 27873780 . Per determinare se il lavoro è in esecuzione, utilizzare le API del 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.
  ...
}

Fornire 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 . Per esempio,

base.apk.prof

Il nome file del .dm deve essere il nome file dell'APK con l'estensione sostituita da .dm . Per 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ò eventualmente utilizzare. La seconda riga esegue dexopt con speed-profile e passa -v per stampare il risultato dettagliato.

Se viene utilizzato il profilo, nel risultato viene visualizzato actualCompilerFilter=speed-profile . Altrimenti, vedrai actualCompilerFilter=verify . Per 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 ART Service non utilizza il profilo includono quanto segue:

  • Il profilo ha un nome file errato o non è accanto all'APK.
  • Il profilo è nel formato sbagliato.
  • Il profilo non corrisponde all'APK. (I checksum nel profilo non corrispondono ai checksum dei file .dex nell'APK.)