Rendimento e visualizzazione delle gerarchie

Il modo in cui gestisci la gerarchia degli oggetti View può influire notevolmente sulle prestazioni della tua app. Questa pagina descrive come valutare se la gerarchia delle visualizzazioni sta rallentando l'applicazione e offre alcune strategie per risolvere i problemi che potrebbero verificarsi.

Questa pagina è incentrata sul miglioramento dei layout basati su View. Per informazioni su come migliorare le prestazioni di Jetpack Compose, consulta Prestazioni di Jetpack Compose.

Layout e misurazione del rendimento

La pipeline di rendering include una fase di layout e misurazione, durante la quale il sistema posiziona in modo appropriato gli elementi pertinenti nella gerarchia delle visualizzazioni. La parte relativa alla misurazione di questa fase determina le dimensioni e i confini degli oggetti View. La parte layout determina dove posizionare gli oggetti View sullo schermo.

Entrambe queste fasi della pipeline comportano un piccolo costo per visualizzazione o layout elaborati. Nella maggior parte dei casi, si tratta di un costo minimo che non influisce in modo significativo sulle prestazioni. Tuttavia, potrebbe essere maggiore quando un'app aggiunge o rimuove oggetti View, ad esempio quando un oggetto RecyclerView li ricicla o li riutilizza. Il costo può anche essere superiore se un oggetto View deve ridimensionarlo per soddisfare i vincoli. Ad esempio, se la tua app chiama SetText() su un oggetto View che va a capo nel testo, è possibile che sia necessario ridimensionare View.

Se casi come questi richiedono troppo tempo, possono impedire il rendering di un frame entro i 16 ms consentiti, il che può causare il blocco dei frame e rendere l'animazione scadente.

Poiché non puoi spostare queste operazioni in un thread di lavoro (la tua app deve elaborarle nel thread principale), è meglio ottimizzarle in modo che richiedano il minor tempo possibile.

Gestisci layout complessi

I Layout di Android consentono di nidificare gli oggetti dell'interfaccia utente nella gerarchia delle visualizzazioni. Questa nidificazione può comportare anche un costo per il layout. Quando l'app elabora un oggetto per il layout, l'app esegue lo stesso processo anche su tutti gli elementi secondari del layout.

Nel caso di un layout complicato, a volte si verifica un costo solo la prima volta che il sistema lo calcola. Ad esempio, quando l'app ricicla un elemento di elenco complesso in un oggetto RecyclerView, il sistema deve disporre tutti gli oggetti. In un altro esempio, modifiche banali possono propagare la catena all'elemento padre fino a quando non raggiungono un oggetto che non influisce sulle dimensioni dell'elemento padre.

Spesso il layout richiede molto tempo quando le gerarchie di oggetti View sono nidificate l'una nell'altra. Ogni oggetto di layout nidificato aggiunge un costo alla fase di layout. Più la gerarchia è piatta, minore è il tempo necessario per il completamento della fase di layout.

Ti consigliamo di utilizzare l'Editor di layout per creare una ConstraintLayout, anziché RelativeLayout o LinearLayout, poiché in genere è più efficiente e riduce la nidificazione dei layout. Tuttavia, per layout semplici ottenuti utilizzando FrameLayout, consigliamo l'uso di FrameLayout.

Se utilizzi la classe RelativeLayout, potresti ottenere lo stesso effetto a un costo inferiore utilizzando invece le viste LinearLayout nidificate e non ponderate. Tuttavia, se utilizzi viste LinearLayout ponderate e nidificate, il costo del layout è molto più elevato perché richiede più passaggi per il layout, come spiegato nella sezione successiva.

Ti consigliamo inoltre di utilizzare RecyclerView anziché ListView, poiché consente di riciclare i layout di singoli elementi dell'elenco, il che è più efficiente e può migliorare le prestazioni dello scorrimento.

Doppia tassazione

In genere, il framework esegue il layout o la fase di misurazione in un solo passaggio. Tuttavia, con alcuni casi di layout complessi, il framework potrebbe dover ripetere più volte su parti della gerarchia che richiedono più passaggi per essere risolti prima di posizionare gli elementi. La necessità di eseguire più iterazioni di layout e misurazione è detta doppia tassazione.

Ad esempio, quando utilizzi il contenitore RelativeLayout, che consente di posizionare View oggetti rispetto alle posizioni di altri oggetti View, il framework esegue la seguente sequenza:

  1. Esegue un passaggio di layout e misurazione durante il quale il framework calcola la posizione e le dimensioni di ogni oggetto figlio in base alla richiesta di ogni oggetto figlio.
  2. Utilizza questi dati, tenendo conto delle ponderazioni degli oggetti, per determinare la posizione corretta delle viste correlate.
  3. Esegue un secondo passaggio del layout per finalizzare le posizioni degli oggetti.
  4. Passa alla fase successiva del processo di rendering.

Maggiore è il numero di livelli della gerarchia delle viste, maggiore è il potenziale di peggioramento delle prestazioni.

Come accennato in precedenza, ConstraintLayout è generalmente più efficiente di altri layout tranne FrameLayout. È meno soggetto a più passaggi di layout e in molti casi elimina la necessità di nidificare i layout.

Anche i container diversi da RelativeLayout potrebbero aumentare la doppia tassazione. Ad esempio:

  • Una visualizzazione LinearLayout può comportare un doppio passaggio di layout e misurazione se la imposti in orizzontale. Un doppio passaggio per layout e misurazione potrebbe verificarsi anche in orientamento verticale se aggiungi measureWithLargestChild, nel qual caso il framework potrebbe dover eseguire una seconda passaggio per risolvere le dimensioni corrette degli oggetti.
  • GridLayout consente anche il posizionamento relativo, ma in genere evita la doppia tassazione pre-elaborando le relazioni di posizionamento tra le viste secondarie. Tuttavia, se il layout utilizza ponderazioni o il riempimento con la classe Gravity, il vantaggio della pre-elaborazione va perso e il framework potrebbe dover eseguire più passaggi se il container è un RelativeLayout.

Più passaggi per layout e misurazione non comportano necessariamente un carico di lavoro per le prestazioni. Tuttavia, possono diventare un peso se si trovano nel posto sbagliato. Fai attenzione alle situazioni in cui una delle seguenti condizioni si applica al tuo contenitore:

  • È un elemento principale nella gerarchia delle visualizzazioni.
  • Sotto è presente una gerarchia di visualizzazione approfondita.
  • Esistono molte istanze in cui compare la schermata, come nel caso degli oggetti secondari in un oggetto ListView.

Diagnostica i problemi relativi alla gerarchia delle visualizzazioni

Il rendimento del layout è un problema complesso con molti facet. I seguenti strumenti possono aiutarti a identificare i colli di bottiglia delle prestazioni. Alcuni strumenti forniscono informazioni meno definitive ma possono fornire suggerimenti utili.

Perfetto

Perfetto è uno strumento che fornisce dati sul rendimento. Puoi aprire le tracce Android nell'UI di Perfetto.

Rendering GPU

Lo strumento rendering GPU del profilo on-device, disponibile sui dispositivi con Android 6.0 (livello API 23) e versioni successive, può fornirti informazioni concrete sui colli di bottiglia delle prestazioni. Questo strumento consente di vedere quanto tempo impiega la fase di layout e misurazione per ogni frame di rendering. Questi dati possono aiutarti a diagnosticare i problemi di prestazioni del runtime e a determinare quali problemi di layout e misurazione devi risolvere.

Nella rappresentazione grafica dei dati acquisiti, il rendering GPU del profilo utilizza il colore blu per rappresentare il tempo di layout. Per ulteriori informazioni su come utilizzare questo strumento, consulta Profilo della velocità di rendering della GPU.

Lint

Lo strumento Lint di Android Studio può aiutarti a capire le inefficienze nella gerarchia delle visualizzazioni. Per utilizzare questo strumento, seleziona Analizza > Ispeziona codice, come mostrato nella Figura 1.

Figura 1. Seleziona Esamina codice in Android Studio.

Le informazioni sui vari elementi del layout vengono visualizzate in Android > Lint > Prestazioni. Per visualizzare ulteriori dettagli, fai clic su ciascun elemento per espanderlo e visualizzare ulteriori informazioni nel riquadro sul lato destro dello schermo. La figura 2 mostra un esempio di informazioni espanse.

Figura 2. Visualizzazione di informazioni su problemi specifici identificati dallo strumento Lint.

Se fai clic su un elemento, vengono visualizzati i problemi associati all'elemento nel riquadro a destra.

Per saperne di più su argomenti e problemi specifici in quest'area, consulta la documentazione relativa a Lint.

Layout Inspector

Lo strumento Controllo layout di Android Studio fornisce una rappresentazione visiva della gerarchia delle viste della tua app. È un buon modo per esplorare la gerarchia dell'app, fornendo una chiara rappresentazione visiva della catena principale di una determinata vista e consente di ispezionare i layout costruiti dall'app.

Le viste presentate da Controllo layout possono anche aiutare a identificare i problemi di rendimento derivanti dalla doppia tassazione. Può anche fornire un modo per identificare catene profonde di layout nidificati o aree di layout con una grande quantità di elementi secondari nidificati, che possono essere una fonte di costi in termini di prestazioni. In questi casi, le fasi di layout e misurazione possono essere costose e causare problemi di prestazioni.

Per ulteriori informazioni, consulta la sezione Debug del layout con Controllo layout e Convalida del layout.

Risolvere i problemi relativi alla gerarchia delle visualizzazioni

Il concetto fondamentale alla base della risoluzione dei problemi di rendimento che derivano dalle gerarchie di viste può essere difficile nella pratica. Impedire alle gerarchie di viste di imporre sanzioni relative alle prestazioni consiste nell'appiattire la gerarchia delle viste e ridurre la doppia tassazione. Questa sezione illustra le strategie per perseguire questi obiettivi.

Rimuovi layout nidificati ridondanti

ConstraintLayout è una libreria Jetpack con un gran numero di meccanismi diversi per posizionare le viste all'interno del layout. Ciò riduce la necessità di nidificare un ConstaintLayout e può contribuire ad appiattire la gerarchia delle viste. In genere è più semplice suddividere le gerarchie utilizzando ConstraintLayout rispetto ad altri tipi di layout.

Gli sviluppatori spesso utilizzano layout più nidificati del necessario. Ad esempio, un container RelativeLayout potrebbe contenere un singolo container secondario che è anche un container RelativeLayout. Questa nidificazione è ridondante e aggiunge costi superflui alla gerarchia delle visualizzazioni. Lint può segnalarti questo problema, riducendo il tempo di debug.

Adotta un'unione o un'inclusione

Una causa frequente di layout nidificati ridondanti è il tag <include>. Ad esempio, potresti definire un layout riutilizzabile come segue:

<LinearLayout>
    <!-- some stuff here -->
</LinearLayout>

Puoi quindi aggiungere un tag <include> per aggiungere il seguente elemento al contenitore principale:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_bg"
    android:gravity="center_horizontal">

    <include layout="@layout/titlebar"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="@string/hello"
              android:padding="10dp" />

    ...

</LinearLayout>

L'inclusione precedente nidifica inutilmente il primo layout all'interno del secondo.

Il tag <merge> può aiutare a prevenire questo problema. Per informazioni su questo tag, consulta Utilizzare il tag <merge>.

Adotta un layout più economico

Potresti non essere in grado di modificare lo schema di layout esistente in modo che non contenga layout ridondanti. In alcuni casi, l'unica soluzione potrebbe essere appiattire la gerarchia passando a un tipo di layout completamente diverso.

Ad esempio, potresti scoprire che TableLayout offre la stessa funzionalità di un layout più complesso con molte dipendenze posizionali. La libreria Jetpack ConstraintLayout offre funzionalità simili a RelativeLayout, oltre a ulteriori funzionalità per creare layout più piatti ed efficienti.