Analisi e ottimizzazione dell'avvio dell'app

Durante l'avvio, l'app fa la prima impressione sugli utenti. L'avvio dell'app deve essere rapido e visualizzare le informazioni necessarie all'utente per utilizzare l'app. Se l'avvio dell'app richiede troppo tempo, gli utenti potrebbero uscire dall'app perché sono in attesa troppo.

Ti consigliamo di utilizzare la libreria Macrobenchmark per misurare l'avvio. La libreria offre una panoramica e tracce di sistema dettagliate per vedere esattamente cosa succede durante l'avvio.

Le tracce di sistema forniscono informazioni utili su ciò che accade sul dispositivo, aiutandoti a capire cosa sta facendo la tua app durante l'avvio e a identificare le potenziali aree di ottimizzazione.

Per analizzare l'avvio dell'app:

Passaggi per analizzare e ottimizzare l'avvio

Durante l'avvio, le app devono caricare risorse specifiche che sono fondamentali per gli utenti finali. Le risorse non essenziali possono attendere il caricamento fino al completamento dell'avvio.

Per scendere a compromessi in termini di rendimento, considera quanto segue:

  • Utilizza la libreria Macrobenchmark per misurare il tempo impiegato da ogni operazione e identificare i blocchi il cui completamento richiede molto tempo.

  • Verifica che l'operazione a utilizzo intensivo di risorse sia fondamentale per l'avvio dell'app. Se l'operazione può attendere fino a quando l'app non è completamente disegnata, può aiutare a ridurre al minimo i vincoli delle risorse all'avvio.

  • Assicurati che questa operazione venga eseguita all'avvio dell'app. Spesso, le operazioni non necessarie possono essere richiamate da codice legacy o librerie di terze parti.

  • Sposta in background le operazioni a lunga esecuzione, se possibile. I processi in background possono comunque influire sull'utilizzo della CPU durante l'avvio.

Dopo aver esaminato a fondo l'operazione, puoi decidere il compromesso tra il tempo necessario per il caricamento e la necessità di includerla nell'avvio dell'app. Ricorda di includere potenziali modifiche di regressione o interruzione del flusso di lavoro dell'app.

Ottimizza e ripeti la misurazione finché il tempo di avvio della tua app non ti soddisfa. Per saperne di più, consulta Utilizzare le metriche per rilevare e diagnosticare i problemi.

Misurare e analizzare il tempo dedicato alle operazioni principali

Una volta che hai una traccia completa dell'avvio dell'app, controlla il tracciamento e misura il tempo impiegato per le operazioni principali come bindApplication o activityStart. Ti consigliamo di utilizzare Perfetto o Android Studio Profiler per analizzare queste tracce.

Guarda il tempo complessivo trascorso durante l'avvio dell'app per identificare le operazioni che:

  • Occupano ampi intervalli di tempo e possono essere ottimizzati. Ogni millisecondo conta per il rendimento. Ad esempio, cerca i tempi di prelievo di Choreographer, i tempi di aumento dell'aumento del layout, i tempi di caricamento delle librerie, le transazioni Binder o i tempi di caricamento delle risorse. Per iniziare, considera tutte le operazioni che richiedono più di 20 ms.
  • Blocca il thread principale. Per ulteriori informazioni, consulta Esplorazione di un report di Systrace.
  • Non deve essere eseguita durante l'avvio.
  • Puoi attendere fino a quando il primo frame non è stato disegnato.

Esamina ulteriormente ciascuna di queste tracce per individuare eventuali lacune del rendimento.

Identifica le operazioni costose sul thread principale

È buona norma evitare che le operazioni costose, come I/O e l'accesso di rete, rimangano fuori dal thread principale. Questo aspetto è altrettanto importante durante l'avvio dell'app, perché operazioni costose nel thread principale possono rendere l'app non reattiva e ritardare altre operazioni critiche. StrictMode.ThreadPolicy può aiutarti a identificare i casi in cui si verificano operazioni costose sul thread principale. È buona norma attivare StrictMode sulle build di debug per identificare i problemi il prima possibile, come mostrato nell'esempio seguente:

Kotlin

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        ...
        if (BuildConfig.DEBUG)
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectAll()
                    .penaltyDeath()
                    .build()
            )
        ...
    }
}

Java

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ...
        if(BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                    new StrictMode.ThreadPolicy.Builder()
                            .detectAll()
                            .penaltyDeath()
                            .build()
            );
        }
        ...
    }
}

L'utilizzo di StrictMode.ThreadPolicy attiva il criterio dei thread su tutte le build di debug e causa l'arresto anomalo dell'app ogni volta che vengono rilevate violazioni dei criteri dei thread, il che rende difficile non rilevare le violazioni dei criteri dei thread.

TTID e TTFD

Per conoscere il tempo necessario all'app per produrre il primo frame, misura il tempo per la visualizzazione iniziale (TTID). Tuttavia, questa metrica non riflette necessariamente il tempo necessario prima che l'utente possa iniziare a interagire con l'app. La metrica Tempo di visualizzazione completa (TTFD) è più utile per misurare e ottimizzare i percorsi di codice necessari per avere uno stato dell'app completamente utilizzabile.

Per strategie relative ai report quando l'interfaccia utente dell'app è completamente disegnata, consulta Migliorare la precisione dei tempi di avvio.

Ottimizza sia per TTID che per TTFD, perché entrambi sono importanti nelle rispettive aree. Un breve TTID aiuta l'utente a capire che l'app è in fase di avvio. Mantenere breve il testo TTFD è importante per garantire che l'utente possa iniziare a interagire rapidamente con l'app.

Analizza lo stato complessivo del thread

Seleziona il tempo di avvio dell'app ed esamina le sezioni complessive dei thread. Il thread principale deve essere sempre reattivo.

Strumenti come Android Studio Profiler e Perfetto forniscono una panoramica dettagliata del thread principale e del tempo trascorso in ogni fase. Per ulteriori informazioni su come visualizzare le tracce esatte, consulta la documentazione di Perfetto UI.

Identificare i blocchi principali dello stato di sospensione del thread principale

Se passi molto tempo a dormire, è probabile che la causa sia l'attesa del completamento del lavoro da parte del thread principale dell'app. Se hai un'app multithread, identifica il thread in attesa del thread principale e valuta la possibilità di ottimizzare queste operazioni. Può anche essere utile assicurarsi che non ci sia una contesa di blocco non necessaria che causa ritardi nel percorso critico.

Riduzione del blocco dei thread principale e della sospensione del gruppo di continuità

Cerca ogni istanza del thread principale in uno stato bloccato. Perfetto e Studio Profiler lo mostrano con un indicatore arancione sulla sequenza temporale dello stato del thread. Identifica le operazioni, scopri se sono previste o evitebili e ottimizzale ove necessario.

Il sonno interrompibile correlato all'IO può essere davvero un'ottima opportunità di miglioramento. Altri processi che eseguono l'IO, anche se sono app non correlate, possono competere con l'IO dell'app principale.

Migliora il tempo di avvio

Dopo aver identificato un'opportunità di ottimizzazione, esplora le possibili soluzioni per migliorare i tempi di avvio:

  • Carica i contenuti in modo lento e asincrono per velocizzare TTID.
  • Riduci al minimo le funzioni di chiamata che effettuano chiamate a Binder. Se sono inevitabili, assicurati di ottimizzare queste chiamate memorizzando nella cache i valori anziché ripetere le chiamate o spostare il lavoro non bloccante in thread in background.
  • Per velocizzare l'avvio dell'app, puoi mostrare all'utente elementi che richiedono un rendering minimo il più rapidamente possibile fino al termine del caricamento del resto dello schermo.
  • Crea e aggiungi un profilo di avvio alla tua app.
  • Utilizza la libreria App Startup di Jetpack per semplificare l'inizializzazione dei componenti durante l'avvio dell'app.

Analizzare le prestazioni dell'interfaccia utente

L'avvio dell'app include una schermata iniziale e il tempo di caricamento della home page. Per ottimizzare l'avvio dell'app, esamina le tracce per comprendere il tempo necessario per tracciare la UI.

Limitare il lavoro sull'inizializzazione

Il caricamento di alcuni frame potrebbe richiedere più tempo di altri. Sono considerate estrazioni costose per l'app.

Per ottimizzare l'inizializzazione:

  • Dai la priorità ai pass per il layout lento e sceglili per migliorarli.
  • Esamina ogni avviso di Perfetto e quello di Systrace aggiungendo eventi di traccia personalizzati per ridurre costosi estrazioni e ritardi.

Misurare i dati del frame

Esistono diversi modi per misurare i dati dei frame. I cinque principali metodi di raccolta sono:

  • Raccolta locale utilizzando dumpsys gfxinfo: non tutti i frame osservati nei dati di dumpsys sono responsabili del rendering lento della tua app o hanno un impatto sugli utenti finali. Tuttavia, questa è una buona misura da esaminare nei diversi cicli di rilascio per comprendere la tendenza generale delle prestazioni. Per scoprire di più sull'utilizzo di gfxinfo e framestats per integrare le misurazioni delle prestazioni dell'interfaccia utente nelle pratiche di test, consulta i Nozioni di base sui test delle app per Android.
  • Raccolta campi utilizzando JankStats: raccogli i tempi di rendering dei frame da parti specifiche della tua app con la libreria JankStats, quindi registra e analizza i dati.
  • Nei test effettuati con Macrobenchmark (Perfetto in background)
  • Perfetto FrameTimeline: Su Android 12 (livello API 31), puoi raccogliere metriche della sequenza temporale dei frame da una traccia Perfetto per cui l'operazione causa il calo di frame. Questo può essere il primo passo per diagnosticare il motivo della perdita di frame.
  • Profilor di Android Studio per il rilevamento di jank

Controllare il tempo di caricamento delle attività principali

L'attività principale della tua app potrebbe contenere una grande quantità di informazioni che vengono caricate da più origini. Controlla la disposizione della casa Activity e in particolare il metodo Choreographer.onDraw dell'attività in casa.

  • Utilizza reportFullyDrawn per segnalare al sistema che la tua app è ora completamente progettata per scopi di ottimizzazione.
  • Misurare l'attività e gli avvii di app utilizzando StartupTimingMetric con la libreria Macrobenchmark.
  • Osserva i cali di frame.
  • Identifica i layout che richiedono molto tempo per il rendering o la misurazione.
  • Identifica gli asset che richiedono molto tempo per il caricamento.
  • Identifica layout non necessari che vengono gonfiati durante l'avvio.

Prendi in considerazione queste possibili soluzioni per ottimizzare il tempo di caricamento delle attività principali:

  • Rendi il tuo layout iniziale il più semplice possibile. Per ulteriori informazioni, consulta Ottimizzare le gerarchie di layout.
  • Aggiungi tracce personalizzate per fornire maggiori informazioni sui frame disattivati e layout complessi.
  • Riduci al minimo il numero e le dimensioni delle risorse bitmap caricate durante l'avvio.
  • Utilizza ViewStub quando i layout non sono immediatamente VISIBLE. Una ViewStub è una visualizzazione invisibile di dimensioni zero che può essere utilizzata per gonfiare pigramente le risorse di layout in fase di runtime. Per ulteriori informazioni, consulta ViewStub.

    Se utilizzi Jetpack Compose, puoi avere un comportamento simile a ViewStub utilizzando lo stato per rimandare il caricamento di alcuni componenti:

    var shouldLoad by remember {mutableStateOf(false)}
    
    if (shouldLoad) {
     MyComposable()
    }
    

    Carica gli elementi componibili all'interno del blocco condizionale modificando shouldLoad:

    LaunchedEffect(Unit) {
     shouldLoad = true
    }
    

    Ciò attiva una ricomposizione che include il codice all'interno del blocco condizionale nel primo snippet.