Questo documento fornisce best practice e passaggi per la risoluzione dei problemi per diagnosticare i problemi e assicurarsi che i profili di base funzionino correttamente per fornire il massimo vantaggio.
Problemi di compilazione
Se hai copiato l'esempio di profili di base nell'app di esempio Now in Android, potresti riscontrare errori di test durante l'attività Profilo di base che indica che i test non possono essere eseguiti su un emulatore:
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
Gli errori si verificano perché Now in Android utilizza un dispositivo gestito da Gradle per la generazione del profilo di base. Gli errori sono previsti perché in genere non devi eseguire benchmark delle prestazioni su un emulatore. Tuttavia, poiché non raccogli le metriche delle prestazioni quando generi i profili di base, puoi eseguire la raccolta dei profili di base sugli emulatori per comodità. Per utilizzare i profili di base con un emulatore, esegui la build e l'installazione dalla riga di comando e imposta un argomento per attivare le regole dei profili di base:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
In alternativa, puoi creare una configurazione di esecuzione personalizzata in Android Studio per attivare i profili di base sugli emulatori selezionando Esegui > Modifica configurazioni:
Verificare l'installazione e l'applicazione del profilo
Per verificare che l'APK o l'Android App Bundle (AAB) che stai esaminando provenga da una variante di build che include i profili di base:
- In Android Studio, seleziona Build > Analyze APK.
- Apri l'AAB o l'APK.
Verifica che il file
baseline.profesista:- Se stai esaminando un AAB, il profilo si trova in
/BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof. Se stai esaminando un APK, il profilo si trova in
/assets/dexopt/baseline.prof.La presenza di questo file è il primo segno di una configurazione di build corretta. Se manca, significa che Android Runtime non riceverà istruzioni di precompilazione al momento dell'installazione.
Figura 2. Controlla la presenza di un profilo di base utilizzando lo strumento di analisi APK in Android Studio.
- Se stai esaminando un AAB, il profilo si trova in
I profili di base devono essere compilati sul dispositivo su cui è in esecuzione l'app. Quando installi build non eseguibili in modalità di debug utilizzando Android Studio o lo strumento a riga di comando Gradle Wrapper, la compilazione sul dispositivo avviene automaticamente. Se installi
l'app dal Google Play Store, i profili di base vengono compilati durante
gli aggiornamenti in background del dispositivo anziché al momento dell'installazione. Quando l'app viene installata
utilizzando altri strumenti, la libreria Jetpack ProfileInstaller è responsabile
dell'accodamento dei profili per la compilazione durante il successivo processo di ottimizzazione DEX
in background.
In questi casi, se vuoi assicurarti che i profili di base vengano utilizzati,
potresti dover forzare la compilazione dei profili di base.
ProfileVerifier consente di eseguire query sullo stato dell'installazione e
della compilazione del profilo, come mostrato nell'esempio seguente:
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
I seguenti codici di risultato forniscono suggerimenti sulla causa di alcuni problemi:
RESULT_CODE_COMPILED_WITH_PROFILE- Il profilo viene installato, compilato e utilizzato ogni volta che viene eseguita l'app. Questo è il risultato che vuoi visualizzare.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED- Nessun profilo trovato nell'APK in esecuzione. Se visualizzi questo errore, assicurati di utilizzare una variante di build che includa i profili di base e che l'APK contenga un profilo.
RESULT_CODE_NO_PROFILE- Nessun profilo è stato installato per questa app durante l'installazione tramite l'app
store o il gestore dei pacchetti. Il motivo principale di questo codice di errore è che il programma di installazione del profilo non è stato eseguito perché
ProfileInstallerInitializerè stato disattivato. Tieni presente che quando viene segnalato questo errore, è stato comunque trovato un profilo incorporato nell'APK dell'applicazione. Quando non viene trovato un profilo incorporato, il codice di errore restituito èRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION- Un profilo viene trovato nell'APK o nell'AAB e viene messo in coda per la compilazione. Quando un profilo viene installato da
ProfileInstaller, viene messo in coda per la compilazione la volta successiva che il sistema esegue l'ottimizzazione DEX in background. Il profilo non è attivo finché la compilazione non viene completata. Non tentare di eseguire il benchmark dei profili di base finché la compilazione non è completata. Potrebbe essere necessario forzare la compilazione dei profili di base. Questo errore non si verifica quando l'app viene installata da Play Store o dal gestore di pacchetti sui dispositivi con Android 9 (API 28) e versioni successive, perché la compilazione viene eseguita durante l'installazione. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING- È installato un profilo non corrispondente e l'app è stata compilata con questo profilo.
Questo è il risultato dell'installazione tramite Google Play Store o package manager.
Tieni presente che questo risultato è diverso da
RESULT_CODE_COMPILED_WITH_PROFILEperché il profilo non corrispondente compilerà solo i metodi ancora condivisi tra il profilo e l'app. Il profilo è effettivamente più piccolo del previsto e verranno compilati meno metodi rispetto a quelli inclusi nel profilo di base. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILEProfileVerifiernon può scrivere il file della cache dei risultati della verifica. Questo può accadere perché si è verificato un problema con le autorizzazioni della cartella dell'app o se non c'è spazio libero su disco sufficiente sul dispositivo.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifiersupporta solo Android 9 (livello API 28) e versioni successive. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST- Viene generata un'
PackageManager.NameNotFoundExceptionquando viene eseguita una query suPackageManagerper il pacchetto dell'app. Questo dovrebbe accadere raramente. Prova a disinstallare l'app e reinstallare tutto. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ- Esiste un file di cache dei risultati di verifica precedenti, ma non può essere letto. Questo dovrebbe accadere raramente. Prova a disinstallare l'app e a reinstallare tutto.
Utilizzare ProfileVerifier in produzione
In produzione, puoi utilizzare ProfileVerifier insieme alle
librerie di generazione di report di analisi, come Google Analytics for Firebase, per
generare eventi di analisi che indicano lo stato del profilo. Ad esempio, questo
ti avvisa rapidamente se viene rilasciata una nuova versione dell'app che non contiene
profili di base.
Forza la compilazione dei profili di base
Se lo stato di compilazione dei profili di base è
RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION, puoi forzare la compilazione
immediata utilizzando adb:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Controlla lo stato di compilazione del profilo di base senza ProfileVerifier
Se non utilizzi ProfileVerifier, puoi controllare lo stato della compilazione utilizzando
adb, anche se non fornisce informazioni approfondite come ProfileVerifier:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
L'utilizzo di adb produce un risultato simile al seguente:
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
Il valore dello stato indica lo stato di compilazione del profilo ed è uno dei seguenti valori:
| Stato della compilazione | Significato |
|---|---|
speed‑profile |
Esiste un profilo compilato e viene utilizzato. |
verify |
Non esiste alcun profilo compilato. |
Lo stato verify non significa che l'APK o l'AAB non contenga un profilo,
perché può essere messo in coda per la compilazione dalla successiva attività di ottimizzazione DEX in background.
Il valore del motivo indica cosa attiva la compilazione del profilo ed è uno dei seguenti valori:
| Motivo | Significato |
|---|---|
install‑dm
|
Un profilo di base è stato compilato manualmente o da Google Play durante l'installazione dell'app. |
bg‑dexopt
|
È stato compilato un profilo mentre il dispositivo era inattivo. Potrebbe trattarsi di un profilo di base o di un profilo raccolto durante l'utilizzo dell'app. |
cmdline
|
La compilazione è stata attivata utilizzando adb. Potrebbe trattarsi di un profilo di base o di un profilo raccolto durante l'utilizzo dell'app. |
Verifica dell'applicazione del profilo di avvio a DEX e r8.json
Le regole del profilo di avvio vengono utilizzate al momento della creazione da R8 per ottimizzare il layout delle classi nei file DEX. Questa ottimizzazione in fase di compilazione è diversa da come vengono utilizzati i profili di base (baseline.prof), in quanto sono inclusi nell'APK o nell'AAB per consentire ad ART di eseguire la compilazione sul dispositivo. Poiché le regole del profilo di avvio
vengono applicate durante la procedura di build, non esiste un file
startup.prof separato all'interno dell'APK o dell'AAB da esaminare. L'effetto dei profili di avvio è visibile nel layout del file DEX.
Ispeziona la disposizione DEX con r8.json (consigliato per AGP 8.8 o versioni successive)
Per i progetti che utilizzano il plug-in Android per Gradle (AGP) 8.8 o versioni successive, puoi verificare
se il profilo di avvio è stato applicato esaminando il file r8.json
generato. Questo file è incluso nel tuo AAB.
- Apri l'archivio AAB e individua il file
r8.json. - Cerca nel file l'array
dexFiles, che elenca i file DEX generati. Cerca un oggetto
dexFilesche contenga la coppia chiave-valore"startup": true. Ciò indica esplicitamente che le regole del profilo di avvio sono state applicate per ottimizzare il layout di quel file DEX specifico."dexFiles": [ { "checksum": "...", "startup": true // This flag confirms profile application to this DEX file }, // ... other DEX files ]
Controlla la disposizione DEX per tutte le versioni di AGP
Se utilizzi una versione di AGP precedente alla 8.8, l'ispezione dei file DEX è il modo principale per verificare che il profilo di avvio sia stato applicato correttamente. Puoi utilizzare questo metodo anche se utilizzi AGP 8.8 o versioni successive e vuoi controllare manualmente il layout DEX. Ad esempio, se non noti i miglioramenti previsti del rendimento. Per esaminare l'accordo DEX:
- Apri l'AAB o l'APK utilizzando Build > Analyze APK (Build > Analizza APK) in Android Studio.
- Vai al primo file DEX. Ad esempio,
classes.dex. - Ispeziona i contenuti di questo file DEX. Dovresti essere in grado di verificare che le
classi e i metodi critici definiti nel file del profilo di avvio
(
startup-prof.txt) siano presenti in questo file DEX principale. Un'applicazione riuscita significa che questi componenti critici per le startup vengono assegnati la priorità per un caricamento più rapido.
Problemi di prestazioni
Questa sezione mostra alcune best practice per definire e confrontare correttamente i profili di base per ottenere il massimo vantaggio.
Eseguire correttamente il benchmarking delle metriche di avvio
I profili di base saranno più efficaci se le metriche di avvio sono ben definite. Le due metriche chiave sono il tempo di attesa per la prima schermata (TTID) e il tempo di attesa per la visualizzazione completa (TTFD).
Il TTID si verifica quando l'app disegna il primo frame. È importante mantenere questo valore il più breve possibile perché la visualizzazione di qualcosa mostra all'utente che l'app è in esecuzione. Puoi anche visualizzare un indicatore di avanzamento indeterminato per mostrare che l'app è reattiva.
TTFD è il momento in cui è possibile interagire effettivamente con l'app. È importante mantenere questo periodo il più breve possibile per evitare la frustrazione degli utenti. Se segnali correttamente TTFD, indichi al sistema che il codice eseguito durante il caricamento di TTFD fa parte dell'avvio dell'app. Di conseguenza, il sistema ha più probabilità di inserire questo codice nel profilo.
Mantieni TTID e TTFD il più bassi possibile per rendere la tua app reattiva.
Il sistema è in grado di rilevare l'ID pubblicità, visualizzarlo in Logcat e segnalarlo come parte
dei benchmark di avvio. Tuttavia, il sistema non è in grado di determinare il TTFD ed è
responsabilità dell'app segnalare quando raggiunge uno stato interattivo
completamente disegnato. Puoi farlo chiamando reportFullyDrawn() o
ReportDrawn se utilizzi Jetpack Compose. Se hai più
attività in background che devono essere completate prima che l'app venga considerata completamente
disegnata, puoi utilizzare FullyDrawnReporter, come descritto in Migliorare
l'accuratezza della tempistica di avvio.
Profili della biblioteca e profili personalizzati
Quando si esegue il benchmarking dell'impatto dei profili, può essere difficile separare i vantaggi dei profili della tua app da quelli forniti da librerie, come le librerie Jetpack. Quando crei l'APK, il plug-in Android per Gradle aggiunge tutti i profili nelle dipendenze di libreria, nonché il tuo profilo personalizzato. Questa opzione è utile per ottimizzare le prestazioni complessive ed è consigliata per le build di rilascio. Tuttavia, è difficile misurare l'aumento di rendimento aggiuntivo derivante dal tuo profilo personalizzato.
Un modo rapido per visualizzare manualmente l'ottimizzazione aggiuntiva fornita dal tuo profilo personalizzato è rimuoverlo ed eseguire i benchmark. Quindi, sostituiscilo ed esegui di nuovo i benchmark. Il confronto tra i due mostra le ottimizzazioni fornite dai soli profili delle librerie e dai profili delle librerie più il tuo profilo personalizzato.
Un modo automatizzabile per confrontare i profili è creare una nuova variante di build che
contenga solo i profili della libreria e non il tuo profilo personalizzato. Confronta
i benchmark di questa variante con la variante di rilascio che contiene sia i
profili delle librerie sia i tuoi profili personalizzati. L'esempio seguente mostra come
configurare la variante che include solo i profili della raccolta. Aggiungi una nuova variante
denominata releaseWithoutCustomProfile al modulo consumer del profilo, che
in genere è il modulo dell'app:
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
L'esempio di codice precedente rimuove la dipendenza baselineProfile da tutte le
varianti e la applica selettivamente solo alla variante release. Potrebbe sembrare
controintuitivo che i profili della libreria vengano ancora aggiunti quando
viene rimossa la dipendenza dal modulo del produttore del profilo. Tuttavia, questo modulo è
responsabile solo della generazione del tuo profilo personalizzato. Il plug-in Android Gradle
è ancora in esecuzione per tutte le varianti ed è responsabile dell'inclusione
dei profili delle librerie.
Devi anche aggiungere la nuova variante al modulo del generatore di profili. In questo
esempio, il modulo del produttore si chiama :baselineprofile.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Quando esegui il benchmark da Android Studio, seleziona una variante releaseWithoutCustomProfile per misurare il rendimento solo con i profili della libreria oppure seleziona una variante release per misurare il rendimento con i profili della libreria e quelli personalizzati.
Evita l'avvio di app con I/O limitato
Se la tua app esegue molte chiamate I/O o di rete durante l'avvio, ciò può influire negativamente sia sul tempo di avvio dell'app sia sull'accuratezza del benchmarking dell'avvio. Queste chiamate pesanti possono richiedere quantità di tempo indeterminate che possono variare nel tempo e persino tra le iterazioni dello stesso benchmark. Le chiamate I/O sono generalmente migliori delle chiamate di rete, perché queste ultime possono essere influenzate da fattori esterni al dispositivo e sul dispositivo stesso. Evita chiamate di rete durante l'avvio. Se l'utilizzo di uno o dell'altro è inevitabile, utilizza I/O.
Ti consigliamo di fare in modo che l'architettura dell'app supporti l'avvio dell'app senza chiamate di rete o I/O, anche se solo per utilizzarla durante il benchmarking dell'avvio. In questo modo, si garantisce la variabilità più bassa possibile tra le diverse iterazioni dei benchmark.
Se la tua app utilizza Hilt, puoi fornire implementazioni finte associate a operazioni di I/O durante il benchmarking in Microbenchmark e Hilt.
Coprire tutti i percorsi utente importanti
È importante coprire con precisione tutti i percorsi utente importanti nella generazione del profilo di base. I percorsi utente non coperti non verranno migliorati dai profili di base. I profili di base più efficaci includono tutti i percorsi utente di avvio comuni, nonché i percorsi utente in-app sensibili alle prestazioni, come lo scorrimento degli elenchi.
Modifiche al profilo in fase di compilazione dei test A/B
Poiché i profili di avvio e di base sono un'ottimizzazione in fase di compilazione, in genere non è supportato il test A/B diretto di APK diversi utilizzando il Google Play Store per le release di produzione. Per valutare l'impatto in un ambiente simile a quello di produzione, valuta i seguenti approcci:
Release fuori ciclo: carica una release fuori ciclo su una piccola percentuale della tua base utenti che include solo la modifica del profilo. In questo modo puoi raccogliere metriche reali sulla differenza di rendimento.
Benchmark locale: esegui il benchmark locale della tua app con e senza il profilo applicato. Tuttavia, tieni presente che il benchmarking locale mostra lo scenario migliore per i profili, in quanto non include gli effetti dei profili cloud di ART presenti nei dispositivi di produzione.