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 la tua app e offre alcune strategie per risolvere i problemi che potrebbero sorgere.
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.
Rendimento di layout e misurazione
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 misura di questa fase determina le dimensioni e i limiti 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 elaborato. Nella maggior parte dei casi, si tratta di un costo minimo che non influisce in modo significativo sulle prestazioni. Tuttavia, può 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 valutare il ridimensionamento per soddisfare i suoi vincoli. Ad esempio, se la tua app chiama
SetText()
su un oggetto View
che manda a capo il testo, potrebbe essere necessario ridimensionare il View
.
Casi come questi che richiedono troppo tempo possono impedire il rendering di un frame entro i 16 ms consentiti, il che può causare l'abbandono dei frame e rendere l'animazione incompleta.
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 viste. Questa nidificazione può imporre 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 presenta un costo solo la prima volta che il sistema calcola il layout. Ad esempio, quando l'app ricicla un elemento 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 comporta un aumento del costo per lo stage del layout. Più la gerarchia è piatta, minore è il tempo necessario per completare la 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
ottenibili con
FrameLayout
, ti consigliamo
di utilizzare FrameLayout
.
Se utilizzi la classe RelativeLayout
, potresti ottenere lo stesso effetto a un costo inferiore utilizzando invece 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 del 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, una soluzione più efficiente che migliora le prestazioni di scorrimento.
Doppia imposizione
In genere, il framework esegue la fase del layout o della misurazione in un unico passaggio. Tuttavia, in alcuni casi di layout complessi, il framework potrebbe dover ripetere più volte l'iterazione in parti della gerarchia che richiedono più passaggi per essere risolti prima di posizionare gli elementi. Dovere eseguire più di un'iterazione di layout e misura è definito doppia imposizione.
Ad esempio, quando utilizzi il contenitore RelativeLayout
, che consente di posizionare
gli oggetti View
rispetto alla posizione di altri oggetti View
, il
framework esegue la seguente sequenza:
- Esegue un passaggio di layout e misura, durante il quale il framework calcola la posizione e le dimensioni di ogni oggetto figlio, in base alla richiesta di ogni oggetto secondario.
- Utilizza questi dati, prendendo in considerazione i pesi degli oggetti, per determinare la posizione corretta delle viste correlate.
- Esegue una seconda trasmissione del layout per finalizzare le posizioni degli oggetti.
- 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ù pass di layout e, in molti casi, elimina la necessità di nidificare i layout.
Anche i container diversi da RelativeLayout
potrebbero aumentare la doppia imposizione. 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 aggiungimeasureWithLargestChild
, 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 di solito evita la doppia tassazione in quanto pre-elabora le relazioni di posizionamento tra le viste secondarie. Tuttavia, se il layout utilizza ponderazioni o riempimento con la classeGravity
, il vantaggio della pre-elaborazione andrà perso e il framework potrebbe dover eseguire più passaggi se il container è unRelativeLayout
.
Più pass di layout e misurazione non rappresentano necessariamente un peso in termini di prestazioni. Tuttavia, possono diventare un peso se si trovano nel posto sbagliato. Presta attenzione alle situazioni in cui al contenitore si applica una delle seguenti condizioni:
- È un elemento principale nella gerarchia delle visualizzazioni.
- Ha una gerarchia di visualizzazione approfondita alla base.
- Esistono molte istanze in cui si completa la schermata, in modo simile agli elementi secondari in un oggetto
ListView
.
Diagnosticare 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 sulle prestazioni. Puoi aprire le tracce di Android nell'UI di Perfetto.
Rendering GPU
Lo strumento di rendering della 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 ti consente di vedere quanto tempo impiega la fase di layout e misurazione per ogni frame del rendering. Questi dati possono aiutarti a diagnosticare i problemi di prestazioni di 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.
Pelucchi
Lo strumento Lint di Android Studio può aiutarti a comprendere le inefficienze nella gerarchia delle visualizzazioni. Per utilizzare questo strumento, seleziona Analizza > Ispeziona codice, come mostrato nella Figura 1.
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.
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 di Lint.
Controllo layout
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 della tua app, fornendo una chiara rappresentazione visiva della catena principale di una determinata vista e ti consente di esaminare i layout costruiti dalla tua app.
Le viste presentate da Layout Inspector possono anche aiutare a identificare i problemi di prestazioni derivanti dalla doppia tassazione. Può anche fornire un modo per identificare catene profonde di layout nidificati o aree di layout con un numero elevato di elementi secondari nidificati, il che può essere 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 Eseguire il 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 prestazioni 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 visualizzazioni e ridurre la doppia imposizione. 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. In questo modo si riduce la necessità di nidificare un ConstaintLayout
e si può contribuire ad appiattire la gerarchia
delle visualizzazioni. 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 elemento figlio che è anche un container RelativeLayout
. Questa nidificazione è ridondante e aggiunge costi inutili alla gerarchia delle visualizzazioni. Lint può segnalare questo problema per te, riducendo i tempi di debug.
Adotta l'unione o l'inclusione
Una causa frequente dei layout nidificati ridondanti è il tag <include>
. Ad esempio, potresti definire un layout riutilizzabile come segue:
<LinearLayout> <!-- some stuff here --> </LinearLayout>
Potresti 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 le stesse funzionalità di un layout più complesso con molte dipendenze di posizionamento. La libreria Jetpack
ConstraintLayout
fornisce funzionalità simili a RelativeLayout
, oltre ad altre funzionalità per contribuire a creare
layout più piatti ed efficienti.