Questo documento illustra le best practice per aiutarti a diagnosticare i problemi e assicurarti che i tuoi profili di riferimento funzionino correttamente per ottenere il massimo vantaggio.
Problemi di compilazione
Se hai copiato l'esempio di profili di riferimento nell'app di esempio Ora su Android, potresti riscontrare errori di test durante l'attività del profilo di riferimento che indicano 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 riferimento. Gli errori sono previsti, in quanto in genere non dovresti eseguire benchmark sul rendimento su un emulatore. Tuttavia, poiché non raccolgi le metriche sul rendimento quando generi i profili di baseline, puoi eseguire la raccolta dei profili di baseline su emulatori per comodità. Per utilizzare Profili di riferimento con un emulatore, esegui la compilazione e l'installazione dalla riga di comando e imposta un argomento per attivare le regole di Profili di riferimento:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
In alternativa, puoi creare una configurazione di esecuzione personalizzata in Android Studio per attivare i profili di riferimento sugli emulatori selezionando Esegui > Modifica configurazioni:

Problemi di installazione
Verifica che l'APK o l'AAB che stai esaminando provenga da una variante di build che include i profili di riferimento:
- In Android Studio, seleziona Build > Analizza APK.
- Apri l'AAB o l'APK.
- Se stai controllando un AAB, il profilo si trova all'indirizzo
/BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof
. Se stai esaminando un APK, il profilo si trova in/assets/dexopt/baseline.prof
.

I profili di riferimento devono essere compilati sul dispositivo su cui è in esecuzione l'app. Quando installi l'app utilizzando il Play Store, Android Studio o lo strumento a riga di comando Gradle Wrapper, la compilazione sul dispositivo avviene automaticamente. Quando l'app viene installata utilizzando altri strumenti, la libreria Jetpack ProfileInstaller
è responsabile dell'inserimento in coda dei profili per la compilazione durante la successiva procedura di ottimizzazione DEX in background. In questi casi, se vuoi assicurarti che i profili di riferimento vengano utilizzati, potresti dover forzare la compilazione dei profili di riferimento. ProfileVerifier
ti 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
- Nell'APK in esecuzione non è stato trovato alcun profilo. Se vedi questo errore, assicurati di utilizzare una variante di compilazione che includa i profili di riferimento e che l'APK contenga un profilo.
RESULT_CODE_NO_PROFILE
- Non è stato installato alcun profilo per questa app durante l'installazione tramite lo store o il gestore dei pacchetti. Il motivo principale di questo codice di errore è che il programma di installazione del profilo non è stato eseguito a causa della disattivazione di
ProfileInstallerInitializer
. Tieni presente che quando viene segnalato questo errore, nell'APK dell'applicazione è stato ancora trovato un profilo incorporato. 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 inserito in coda per la compilazione. Quando un
profilo viene installato da
ProfileInstaller
, viene messo in coda per la compilazione la volta successiva che l'ottimizzazione DEX in background viene eseguita dal sistema. Il profilo non è attivo fino al completamento della compilazione. Non tentare di eseguire il benchmark dei profili di riferimento finché la compilazione non è completata. Potresti dover forzare la compilazione dei profili di riferimento. Questo errore non si verifica quando l'app viene installata dall'app store o dal gestore 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 il Google Play Store o il gestore dei pacchetti.
Tieni presente che questo risultato è diverso da
RESULT_CODE_COMPILED_WITH_PROFILE
perché 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 riferimento. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
non riesce a scrivere il file della cache dei risultati di verifica. Questo può accadere perché c'è un problema con le autorizzazioni della cartella dell'app o se non c'è spazio su disco libero sufficiente sul dispositivo.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
supporta solo Android 9 (livello API 28) e versioni successive. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
- Viene generato un
PackageManager.NameNotFoundException
quando viene eseguita una query sulPackageManager
per il pacchetto dell'app. Questo dovrebbe accadere raramente. Prova a disinstallare l'app e a reinstallare tutto. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- Esiste un file della cache dei risultati di verifica precedente, ma non è possibile leggerlo. Questo dovrebbe accadere raramente. Prova a disinstallare l'app e a reinstallare tutto.
Utilizzare ProfileVerifier in produzione
In produzione, puoi utilizzare ProfileVerifier
in combinazione con librerie di generazione di report di analisi, come Google Analytics per Firebase, per generare eventi di analisi che indicano lo stato del profilo. Ad esempio, ti avvisa rapidamente se viene rilasciata una nuova versione dell'app che non contiene Profili di riferimento.
Forzare la compilazione dei profili di riferimento
Se lo stato di compilazione dei profili di baseline è RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
, puoi forzare la compilazione immediata utilizzando adb
:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Controllare lo stato della compilazione senza ProfileVerifier
Se non utilizzi ProfileVerifier
, puoi controllare lo stato della compilazione utilizzando
adb
, anche se non fornisce informazioni così approfondite come ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
L'utilizzo di adb
produce qualcosa di 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 ed è in uso. |
verify |
Non esiste alcun profilo compilato. |
Uno stato verify
non significa che l'APK o l'AAB non contenga un profilo, poiché 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 riferimento è stato compilato manualmente o da Google Play quando l'app è stata installata. |
bg‑dexopt
|
È stato compilato un profilo mentre il dispositivo era inattivo. Potrebbe trattarsi di un profilo di riferimento o di un profilo raccolto durante l'utilizzo dell'app. |
cmdline
|
La compilazione è stata attivata utilizzando adb. Potrebbe trattarsi di un profilo di riferimento o di un profilo raccolto durante l'utilizzo dell'app. |
Problemi di prestazioni
Questa sezione illustra alcune best practice per definire e confrontare correttamente i profili di riferimento per ottenere il massimo vantaggio.
Eseguire correttamente il benchmarking delle metriche di avvio
I profili di riferimento 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).
TTID è il momento in cui l'app disegna il primo frame. È importante che sia il più breve possibile, perché la visualizzazione di qualcosa indica all'utente che l'app è in esecuzione. Puoi anche mostrare un indicatore di avanzamento indeterminato per mostrare che l'app è adattabile.
Il TTFD è il momento in cui è effettivamente possibile interagire con l'app. È importante mantenere questa operazione il più breve possibile per evitare di frustrare gli utenti. Se segnali correttamente il TTFD, comunichi al sistema che il codice eseguito durante il raggiungimento del TTFD fa parte dell'avvio dell'app. Di conseguenza, è più probabile che il sistema inserisca questo codice nel profilo.
Mantieni TTID e TTFD il più bassi possibile per rendere la tua app reattiva.
Il sistema è in grado di rilevare il TTID, visualizzarlo in Logcat e segnalarlo nell'ambito dei benchmark di avvio. Tuttavia, il sistema non è in grado di determinare il TTFD ed è compito dell'app segnalare quando raggiunge uno stato interattivo completamente visualizzato. Puoi farlo chiamando reportFullyDrawn()
o
ReportDrawn
se utilizzi Jetpack Compose. Se hai più attività in background che devono essere completate prima che l'app sia considerata completamente disegnata, puoi utilizzare FullyDrawnReporter
, come descritto in Migliorare la precisione dei tempi di avvio.
Profili della raccolta e profili personalizzati
Quando esegui il benchmarking dell'impatto dei profili, può essere difficile separare i vantaggi dei profili della tua app dai profili forniti dalle librerie, come le librerie Jetpack. Quando compili l'APK, il plug-in Android per Gradle aggiunge eventuali profili nelle dipendenze delle librerie, nonché il tuo profilo personalizzato. Questa opzione è utile per ottimizzare il rendimento complessivo ed è consigliata per le build di release. Tuttavia, è difficile misurare l'aumento del rendimento derivante dal 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. Confrontando i due, vedrai le ottimizzazioni fornite solo dai profili della raccolta e dai profili della raccolta più il tuo profilo personalizzato.
Un modo automatizzato per confrontare i profili è creare una nuova variante di compilazione che contenga solo i profili della raccolta e non il profilo personalizzato. Confronta
i benchmark di questa variante con la variante di release che contiene sia i
profili della libreria sia i tuoi profili personalizzati. L'esempio seguente mostra come configurare la variante che include solo i profili delle biblioteche. Aggiungi una nuova variante
chiamata releaseWithoutCustomProfile
al modulo consumer del profilo, che solitamente è
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 in modo selettivo solo alla variante release
. Potrebbe sembrare controintuitivo che i profili della raccolta vengano ancora aggiunti quando la dipendenza dal modulo del produttore di profili viene rimossa. 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 è denominato :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 personalizzati.
Evita l'avvio di app con vincoli I/O
Se la tua app esegue molte chiamate I/O o di rete durante l'avvio, puoi influire negativamente sia sul tempo di avvio dell'app sia sull'accuratezza del benchmarking dell'avvio. Queste chiamate pesanti possono richiedere tempi indefiniti che possono variare nel tempo e anche tra le iterazioni dello stesso benchmark. Le chiamate I/O sono in genere migliori delle chiamate di rete, perché queste ultime possono essere influenzate da fattori esterni al dispositivo e dal dispositivo stesso. Evita le 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, puoi garantire la minore variabilità possibile tra le diverse iterazioni dei benchmark.
Se la tua app utilizza Hilt, puoi fornire implementazioni legate all'I/O false durante il benchmarking in Microbenchmark e Hilt.
Coprire tutti i percorsi dell'utente importanti
È importante coprire con precisione tutti i percorsi utente importanti nella generazione del profilo di riferimento. I percorsi utente non coperti non verranno migliorati dai profili di riferimento. I profili di riferimento più efficaci includono tutti i percorsi degli utenti iniziali comuni, nonché i percorsi degli utenti in-app sensibili alle prestazioni, come gli elenchi scorrevoli.