Performances et hiérarchies des vues

La manière dont vous gérez la hiérarchie de vos objets View peut avoir un impact significatif sur les performances de votre application. Cette page explique comment évaluer si votre hiérarchie des vues ralentit votre application et propose quelques stratégies pour résoudre les problèmes qui peuvent survenir.

Cette page explique comment améliorer les mises en page basées sur View. Pour en savoir plus sur l'amélioration des performances de Jetpack Compose, consultez la page Performances de Jetpack Compose.

Mise en page et mesure des performances

Le pipeline de rendu comprend une étape de mise en page et de mesure, au cours de laquelle le système positionne correctement les éléments pertinents dans votre hiérarchie des vues. La phase de mesure de cette étape détermine la taille et les limites des objets View. La phase de mise en page détermine l'emplacement des objets View sur l'écran.

Ces deux étapes de pipeline entraînent un faible coût par vue ou mise en page qu'elles traitent. La plupart du temps, ce coût est minime et n'affecte pas sensiblement les performances. Il peut toutefois être plus élevé lorsqu'une application ajoute ou supprime des objets View, par exemple lorsqu'un objet RecyclerView les recycle ou les réutilise. Le coût peut également être plus élevé s'il faut envisager de redimensionner l'objet View pour répondre à ses contraintes. Par exemple, si votre application appelle SetText() sur un objet View qui encapsule le texte, View devra peut-être être redimensionné.

Si ce type de cas prend trop de temps, il peut empêcher une image de s'afficher dans les 16 ms autorisées, ce qui peut entraîner la suppression des images et une animation saccadée

Étant donné que vous ne pouvez pas déplacer ces opérations vers un thread de nœud de calcul (votre application doit les traiter sur le thread principal), la meilleure solution consiste à les optimiser afin qu'elles prennent le moins de temps possible.

Gérer les mises en page complexes

Les mises en page Android vous permettent d'imbriquer des objets d'interface utilisateur dans la hiérarchie des vues. Cette imbrication peut également entraîner un coût de mise en page. Lorsque votre application traite un objet pour la mise en page, elle effectue également le même processus sur tous les enfants de la mise en page.

Lorsque la mise en page est complexe, un coût n'est parfois généré que la première fois que le système calcule la mise en page. Par exemple, lorsque votre application recycle un élément de liste complexe dans un objet RecyclerView, le système doit mettre en page tous les objets. Dans un autre exemple, des modifications mineures peuvent se propager à la chaîne menant au parent jusqu'à ce qu'elles atteignent un objet qui n'affecte pas la taille du parent.

La mise en page prend souvent beaucoup de temps lorsque les hiérarchies d'objets View sont imbriquées. Chaque objet de mise en page imbriqué ajoute un coût à l'étape de mise en page. Plus votre hiérarchie est plate, moins l'étape de mise en page prend du temps.

Nous vous recommandons d'utiliser l'éditeur de mise en page pour créer un ConstraintLayout, au lieu de RelativeLayout ou LinearLayout, car il est généralement à la fois plus efficace et réduit l'imbrication des mises en page. Toutefois, pour les mises en page simples qui peuvent être réalisées à l'aide de FrameLayout, nous vous recommandons d'utiliser FrameLayout.

Si vous utilisez la classe RelativeLayout, vous pouvez obtenir le même effet à moindre coût en utilisant des vues LinearLayout imbriquées et non pondérées. Toutefois, si vous utilisez des vues LinearLayout imbriquées et pondérées, le coût de la mise en page est beaucoup plus élevé, car plusieurs passes de mise en page sont nécessaires, comme expliqué dans la section suivante.

Nous vous recommandons également d'utiliser RecyclerView au lieu de ListView, car il peut recycler la mise en page d'éléments de liste individuels, ce qui est à la fois plus efficace et peut améliorer les performances de défilement.

Double imposition

En règle générale, le framework exécute l'étape de mise en page ou de mesure en une seule fois. Toutefois, dans certains cas de mise en page complexes, le framework peut devoir effectuer une itération plusieurs fois sur les parties de la hiérarchie qui nécessitent une résolution en plusieurs passes avant de positionner les éléments. L'exécution de plusieurs itérations de mise en page et de mesure s'appelle la double imposition.

Par exemple, lorsque vous utilisez le conteneur RelativeLayout, qui vous permet de positionner des objets View par rapport aux positions d'autres objets View, le framework effectue la séquence suivante :

  1. Exécute une passe de mise en page et de mesure au cours de laquelle le framework calcule la position et la taille de chaque objet enfant, en fonction de la requête de chaque enfant.
  2. Utilise ces données en tenant compte des pondérations des objets pour déterminer la position correcte des vues corrélées.
  3. Effectue une deuxième mise en page pour finaliser la position des objets.
  4. Passe à l'étape suivante du processus de rendu.

Plus votre hiérarchie est vaste, plus la pénalité liée aux performances est élevée.

Comme indiqué précédemment, ConstraintLayout est généralement plus efficace que les autres mises en page, à l'exception de FrameLayout. Elle est moins sujette aux multiples passes de mise en page et, dans de nombreux cas, évite d'imbriquer des mises en page.

Les conteneurs autres que RelativeLayout peuvent également augmenter la double imposition. Par exemple :

  • Une vue LinearLayout peut entraîner une double passe de mise en page et de mesure si vous la rendez horizontale. Une double passe de mise en page et de mesure peut également se produire dans une orientation verticale si vous ajoutez measureWithLargestChild, auquel cas le framework devra peut-être effectuer une seconde passe pour résoudre les tailles d'objets appropriées.
  • GridLayout permet également un positionnement relatif, mais il évite normalement la double imposition en prétraitant les relations de position entre les vues enfants. Toutefois, si la mise en page utilise des pondérations ou un remplissage avec la classe Gravity, l'avantage de ce prétraitement est perdu, et le framework devra peut-être effectuer plusieurs passes si le conteneur est un RelativeLayout.

L'utilisation de plusieurs passes de mise en page et de mesure n'affecte pas nécessairement les performances. Cependant, ils peuvent devenir un fardeau s'ils ne se font pas au bon endroit. Soyez prudent lorsque l'une des conditions suivantes s'applique à votre conteneur :

  • Il s'agit d'un élément racine dans la hiérarchie des vues.
  • Il est sous-hiérarchique.
  • Il remplit de nombreuses instances, comme les enfants dans un objet ListView.

Diagnostiquer les problèmes de hiérarchie des vues

Les performances de mise en page sont un problème complexe aux multiples facettes. Les outils suivants peuvent vous aider à identifier les goulots d'étranglement qui affectent les performances. Certains outils fournissent des informations moins définitives, mais peuvent fournir des indications utiles.

Perfetto

Perfetto est un outil qui fournit des données sur les performances. Vous pouvez ouvrir les traces Android dans l'interface utilisateur de Perfetto.

Rendu GPU du profil

L'outil de rendu GPU du profil sur l'appareil, disponible sur les appareils équipés d'Android 6.0 (niveau d'API 23) ou version ultérieure, peut vous fournir des informations concrètes sur les goulots d'étranglement qui affectent les performances. Cet outil vous permet de voir la durée de l'étape de mise en page et de mesure pour chaque image du rendu. Ces données peuvent vous aider à diagnostiquer les problèmes de performances liés à l'exécution et à déterminer les problèmes de mise en page et de mesure à résoudre.

Dans sa représentation graphique des données capturées, le rendu GPU du profil utilise la couleur bleue pour représenter le temps de mise en page. Pour en savoir plus sur l'utilisation de cet outil, consultez la page Vitesse du rendu GPU du profil.

Lint

L'outil lint d'Android Studio peut vous aider à identifier les problèmes d'efficacité dans la hiérarchie des vues. Pour utiliser cet outil, sélectionnez Analyze > Inspect Code (Analyser > Inspecter le code), comme indiqué dans la figure 1.

Figure 1 : Sélectionnez Inspect Code (Inspecter le code) dans Android Studio.

Les informations sur les différents éléments de mise en page s'affichent sous Android > Lint > Performance. Pour afficher plus de détails, cliquez sur chaque élément pour le développer et afficher plus d'informations dans le volet situé à droite de l'écran. La figure 2 montre un exemple d'informations développées.

Figure 2 : Afficher des informations sur des problèmes spécifiques identifiés par l'outil lint

Cliquez sur un élément pour afficher les problèmes qui lui sont associés dans le volet de droite.

Pour en savoir plus sur des sujets et des problèmes spécifiques dans cette zone, consultez la documentation lint.

Outil d'inspection de la mise en page

L'outil d'inspection de la mise en page d'Android Studio fournit une représentation visuelle de la hiérarchie des vues de votre application. C'est un bon moyen de naviguer dans la hiérarchie de votre application. Il offre une représentation visuelle claire de la chaîne parente d'une vue spécifique et vous permet d'inspecter les mises en page construites par votre application.

Les vues proposées par l'outil d'inspection de la mise en page peuvent également vous aider à identifier les problèmes de performances liés à la double imposition. L'outil peut aussi vous permettre d'identifier les chaînes profondes de mises en page imbriquées, ou les zones de mise en page avec un grand nombre d'enfants imbriqués, qui peuvent engendrer des coûts de performances. Dans ce cas, les étapes de mise en page et de mesure peuvent être coûteuses et entraîner des problèmes de performances.

Pour en savoir plus, consultez la page Déboguer votre mise en page avec l'outil d'inspection de la mise en page et la validation de la mise en page.

Résoudre les problèmes liés à la hiérarchie des vues

Dans la pratique, le concept fondamental qui sous-tend la résolution des problèmes de performances découlant des hiérarchies des vues peut s'avérer difficile. Pour éviter que les hiérarchies des vues imposent des pénalités de performances, vous devez aplatir la hiérarchie des vues et réduire la double imposition. Cette section présente des stratégies qui permettent d'atteindre ces objectifs.

Supprimer les mises en page imbriquées redondantes

ConstraintLayout est une bibliothèque Jetpack dotée de nombreux mécanismes différents pour positionner les vues dans la mise en page. Cela évite d'avoir à imbriquer une seule ConstaintLayout et peut vous aider à aplatir la hiérarchie des vues. Il est généralement plus simple d'aplatir les hiérarchies à l'aide de ConstraintLayout que d'autres types de mise en page.

Les développeurs utilisent souvent plus de mises en page imbriquées que nécessaire. Par exemple, un conteneur RelativeLayout peut contenir un seul enfant, qui est également un conteneur RelativeLayout. Cette imbrication est redondante et ajoute un coût inutile à la hiérarchie des vues. Lint peut signaler ce problème pour vous, réduisant ainsi le temps de débogage.

Fusionner ou inclure

La balise <include> est l'une des causes fréquentes de redondances liées aux mises en page imbriquées. Par exemple, vous pouvez définir une mise en page réutilisable comme suit :

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

Vous pouvez ensuite ajouter une balise <include> pour inclure l'élément suivant dans le conteneur parent :

<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>

La balise imbrique inutilement la première mise en page dans la seconde.

La balise <merge> peut vous aider à éviter ce problème. Pour en savoir plus sur cette balise, consultez la page Utiliser la balise <merge>.

Adopter une mise en page moins chère

Vous ne pourrez peut-être pas ajuster votre mise en page existante afin qu'elle ne contienne pas de mises en page redondantes. Dans certains cas, la seule solution peut être d'aplatir votre hiérarchie en basculant vers un type de mise en page complètement différent.

Par exemple, vous constaterez peut-être que TableLayout fournit les mêmes fonctionnalités qu'une mise en page plus complexe avec de nombreuses dépendances de position. La bibliothèque Jetpack ConstraintLayout offre des fonctionnalités semblables à RelativeLayout, ainsi que d'autres fonctionnalités permettant de créer des mises en page plus plates et plus efficaces.