Leistungs- und Ansichtshierarchien

Die Art und Weise, wie Sie die Hierarchie Ihrer View-Objekte verwalten, kann sich erheblich auf die Leistung Ihrer App auswirken. Auf dieser Seite wird beschrieben, wie Sie feststellen können, ob Ihre Ansichtshierarchie Ihre App verlangsamt, und es werden einige Strategien zur Behebung möglicher Probleme vorgestellt.

Auf dieser Seite geht es um die Verbesserung von View-basierten Layouts. Informationen zur Leistungssteigerung von Jetpack Compose finden Sie unter Jetpack Compose-Leistung.

Layout erstellen und Leistung messen

Die Rendering-Pipeline umfasst eine Phase zum Layouten und Messen, in der das System die relevanten Elemente in der Ansichtshierarchie richtig platziert. Mit dem Teil Messen dieser Phase werden die Größen und Grenzen von View-Objekten bestimmt. Im Bereich Layout wird festgelegt, wo auf dem Bildschirm die View-Objekte positioniert werden.

Für beide Pipelinephasen fallen geringe Kosten pro Ansicht oder Layout an. In den meisten Fällen sind diese Kosten minimal und wirken sich nicht merklich auf die Leistung aus. Sie kann jedoch höher sein, wenn eine App View-Objekte hinzufügt oder entfernt, z. B. wenn ein RecyclerView-Objekt sie recycelt oder wiederverwendet. Die Kosten können auch höher sein, wenn die Größe eines View-Objekts angepasst werden muss, um die Einschränkungen zu erfüllen. Wenn Ihre App beispielsweise SetText() auf ein View-Objekt anwendet, das Text umbricht, muss die Größe des View möglicherweise angepasst werden.

Wenn solche Fälle zu lange dauern, können sie verhindern, dass ein Frame innerhalb der zulässigen 16 ms gerendert wird. Dies kann dazu führen, dass Frames ausgelassen werden und die Animation ruckelt.

Da Sie diese Vorgänge nicht in einen Worker-Thread verschieben können, da sie von Ihrer Anwendung im Hauptthread verarbeitet werden müssen, sollten Sie sie so optimieren, dass sie so wenig Zeit wie möglich in Anspruch nehmen.

Komplexe Layouts verwalten

Mit Layouts in Android können Sie UI-Objekte in der Ansichtshierarchie verschachteln. Das Verschachtelungsmuster kann auch Layoutkosten verursachen. Wenn Ihre App ein Objekt für das Layout verarbeitet, führt sie denselben Vorgang auch für alle untergeordneten Elemente des Layouts aus.

Bei einem komplizierten Layout entstehen manchmal nur dann Kosten, wenn das System das Layout zum ersten Mal berechnet. Wenn Ihre Anwendung beispielsweise ein komplexes Listenelement in einem RecyclerView-Objekt wiederverwendet, muss das System alle Objekte anordnen. In einem anderen Beispiel können unwesentliche Änderungen sich bis zum übergeordneten Objekt fortpflanzen, bis sie ein Objekt erreichen, das sich nicht auf die Größe des übergeordneten Objekts auswirkt.

Ein häufiger Grund dafür, dass das Layout lange dauert, ist, wenn Hierarchien von View-Objekten verschachtelt sind. Jedes verschachtelte Layoutobjekt erhöht die Kosten für die Layoutphase. Je flacher die Hierarchie, desto kürzer ist die Layoutphase.

Wir empfehlen die Verwendung des Layout-Editors zum Erstellen eines ConstraintLayout anstelle von RelativeLayout oder LinearLayout, da dies im Allgemeinen sowohl effizienter ist als auch die Verschachtelung von Layouts reduziert. Für einfache Layouts, die mit FrameLayout realisiert werden können, empfehlen wir jedoch die Verwendung von FrameLayout.

Wenn Sie die Klasse RelativeLayout verwenden, können Sie den gleichen Effekt möglicherweise zu niedrigeren Kosten erzielen, wenn Sie stattdessen verschachtelte, ungewichtete LinearLayout-Ansichten verwenden. Wenn Sie jedoch verschachtelte, gewichtete LinearLayout-Aufrufe verwenden, sind die Layoutkosten viel höher, da mehrere Layoutpässe erforderlich sind, wie im nächsten Abschnitt erläutert.

Wir empfehlen außerdem, RecyclerView anstelle von ListView zu verwenden, da die Layouts einzelner Listenelemente wiederverwendet werden können. Das ist effizienter und kann die Scrollleistung verbessern.

Doppelbesteuerung

Normalerweise führt das Framework die Layout- oder Messphase in einem einzigen Durchlauf aus. Bei einigen komplexen Layouts muss das Framework jedoch möglicherweise mehrmals Teile der Hierarchie iterieren, die mehrere Durchläufe erfordern, bevor die Elemente endgültig positioniert werden. Wenn mehr als eine Iteration für Layout und Analyse erforderlich ist, wird dies als doppelte Besteuerung bezeichnet.

Wenn Sie beispielsweise den RelativeLayout-Container verwenden, mit dem Sie View-Objekte relativ zu den Positionen anderer View-Objekte positionieren können, führt das Framework die folgende Abfolge aus:

  1. Führt einen Layout- und Maßdurchlauf aus, bei dem das Framework die Position und Größe jedes untergeordneten Objekts basierend auf der Anfrage des jeweiligen untergeordneten Objekts berechnet.
  2. Anhand dieser Daten und unter Berücksichtigung der Objektgewichte wird die richtige Position der korrespondierenden Ansichten ermittelt.
  3. Führt einen zweiten Layoutdurchlauf aus, um die Positionen der Objekte zu finalisieren.
  4. Es wird mit der nächsten Phase des Rendering-Prozesses fortgefahren.

Je mehr Ebenen die Ansichtshierarchie hat, desto größer ist die Wahrscheinlichkeit, dass die Leistung beeinträchtigt wird.

Wie bereits erwähnt, ist ConstraintLayout im Allgemeinen effizienter als andere Layouts mit Ausnahme von FrameLayout. Sie ist weniger anfällig für mehrere Layoutübergänge und in vielen Fällen ist es nicht erforderlich, Layouts zu verschachteln.

Andere Container als RelativeLayout können auch zu einer doppelten Besteuerung führen. Beispiel:

  • Bei einer LinearLayout-Ansicht kann es zu einem doppelten Layout- und Analysedurchlauf kommen, wenn Sie sie horizontal ausrichten. Ein doppelter Layout- und Messdurchlauf kann auch bei vertikaler Ausrichtung auftreten, wenn Sie measureWithLargestChild hinzufügen. In diesem Fall muss das Framework möglicherweise einen zweiten Durchlauf ausführen, um die richtigen Größen der Objekte zu ermitteln.
  • Die GridLayout ermöglicht auch eine relative Positionierung, aber normalerweise wird eine doppelte Besteuerung vermieden, indem die Positionsbeziehungen zwischen untergeordneten Ansichten vorab verarbeitet werden. Wenn das Layout jedoch Gewichte oder Füllungen mit der Klasse Gravity verwendet, geht der Vorteil der Vorverarbeitung verloren und das Framework muss möglicherweise mehrere Durchläufe ausführen, wenn der Container ein RelativeLayout ist.

Mehrere Layout- und Messpässe sind nicht unbedingt eine Leistungsbelastung. Sie können jedoch zur Belastung werden, wenn sie sich an der falschen Stelle befinden. Seien Sie vorsichtig, wenn eine der folgenden Bedingungen auf Ihren Container zutrifft:

  • Es ist ein Stammelement in Ihrer Ansichtshierarchie.
  • Es hat eine tiefe Ansichtshierarchie.
  • Es gibt viele Instanzen davon auf dem Bildschirm, ähnlich wie untergeordnete Elemente in einem ListView-Objekt.

Probleme mit der Ansichtshierarchie diagnostizieren

Die Layoutleistung ist ein komplexes Problem mit vielen Facetten. Mit den folgenden Tools können Sie Leistungsengpässe ermitteln. Einige Tools liefern weniger bestimmte Informationen, können aber hilfreiche Hinweise liefern.

Perfetto

Perfetto ist ein Tool, das Daten zur Leistung liefert. Sie können Android-Protokolle in der Perfetto-Benutzeroberfläche öffnen.

GPU-Rendering für Profil

Das On-Device-Tool GPU-Rendering profilieren, das auf Geräten mit Android 6.0 (API-Level 23) und höher verfügbar ist, kann Ihnen konkrete Informationen zu Leistungsengpässen liefern. Mit diesem Tool können Sie sehen, wie lange die Layout- und Messphase für jeden Frame des Renderings dauert. Anhand dieser Daten können Sie Laufzeitleistungsprobleme diagnostizieren und feststellen, welche Probleme mit Layout und Maßen behoben werden müssen.

In der grafischen Darstellung der erfassten Daten wird beim GPU-Rendering von Profilen die Farbe Blau für die Layoutzeit verwendet. Weitere Informationen zur Verwendung dieses Tools finden Sie unter GPU-Renderinggeschwindigkeit erfassen.

Flusen

Mit dem Lint-Tool von Android Studio können Sie sich ein Bild von Ineffizienzen in der Ansichtshierarchie machen. Wählen Sie zur Verwendung dieses Tools Analysieren > Code prüfen aus, wie in Abbildung 1 gezeigt.

Abbildung 1: Wählen Sie in Android Studio Code prüfen aus.

Informationen zu verschiedenen Layoutelementen finden Sie unter Android > Lint > Leistung. Wenn Sie weitere Details sehen möchten, klicken Sie auf die einzelnen Elemente, um sie zu maximieren und weitere Informationen im Bereich auf der rechten Seite des Bildschirms anzuzeigen. Abbildung 2 zeigt ein Beispiel für erweiterte Informationen.

Abbildung 2: Informationen zu bestimmten Problemen, die vom Lint-Tool erkannt werden.

Wenn Sie auf einen Artikel klicken, werden im rechten Bereich Probleme angezeigt, die mit diesem Artikel zusammenhängen.

Weitere Informationen zu bestimmten Themen und Problemen in diesem Bereich finden Sie in der Lint-Dokumentation.

Layout Inspector

Das Layout-Inspection-Tool von Android Studio bietet eine visuelle Darstellung der Ansichtshierarchie Ihrer App. Es ist eine gute Möglichkeit, sich in der Hierarchie Ihrer App zu bewegen. Sie erhalten eine klare visuelle Darstellung der übergeordneten Kette einer bestimmten Ansicht und können die Layouts prüfen, die in Ihrer App erstellt werden.

Die Ansichten, die der Layout-Inspektor bietet, können auch dabei helfen, Leistungsprobleme zu erkennen, die durch doppelte Besteuerung entstehen. Sie können damit auch tiefe Verkettungen verschachtelter Layouts oder Layoutbereiche mit einer großen Anzahl verschachtelter untergeordneter Elemente identifizieren, was zu Leistungskosten führen kann. In diesen Fällen können die Layout- und Mess-Phasen kostspielig sein und zu Leistungsproblemen führen.

Weitere Informationen finden Sie unter Layout mit Layout Inspector und Layout Validation debuggen.

Probleme mit der Ansichtshierarchie beheben

Das grundlegende Konzept der Lösung von Leistungsproblemen, die sich aus Ansichtshierarchien ergeben, kann in der Praxis schwierig sein. Um Leistungseinbußen durch Ansichtshierarchien zu vermeiden, sollten Sie die Hierarchie flach halten und doppelte Besteuerung vermeiden. In diesem Abschnitt werden Strategien zur Erreichung dieser Ziele erläutert.

Entfernen Sie redundante verschachtelte Layouts.

ConstraintLayout ist eine Jetpack-Bibliothek mit einer Vielzahl verschiedener Mechanismen zur Positionierung von Ansichten im Layout. Dies reduziert die Notwendigkeit, eine ConstaintLayout zu verschachteln, und kann dazu beitragen, die Ansichtshierarchie zu vereinfachen. Im Vergleich zu anderen Layouttypen ist es mit ConstraintLayout in der Regel einfacher, Hierarchien zu vereinfachen.

Entwickler verwenden oft mehr verschachtelte Layouts als nötig. Ein RelativeLayout-Container kann beispielsweise ein einzelnes untergeordnetes Element enthalten, das ebenfalls ein RelativeLayout-Container ist. Diese Verschachtelung ist redundant und erhöht die Kosten der Ansichtshierarchie unnötig. Lint kann dieses Problem für Sie melden und die Fehlerbehebungszeit verkürzen.

Zusammenführen oder einschließen

Eine häufige Ursache für redundante verschachtelte Layouts ist das Tag <include>. So könnten Sie ein wiederverwendbares Layout definieren:

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

Sie können dann ein <include>-Tag hinzufügen, um dem übergeordneten Container das folgende Element hinzuzufügen:

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

Im vorherigen Include-Tag ist das erste Layout unnötig im zweiten Layout verschachtelt.

Mit dem Tag <merge> lässt sich dieses Problem vermeiden. Weitere Informationen zu diesem Tag finden Sie unter <merge>-Tag verwenden.

Ein kostengünstigeres Layout verwenden

Möglicherweise können Sie Ihr vorhandenes Layoutschema nicht so anpassen, dass es keine redundanten Layouts enthält. In bestimmten Fällen ist die einzige Lösung, die Hierarchie zu vereinfachen, indem Sie zu einem ganz anderen Layouttyp wechseln.

Sie stellen beispielsweise fest, dass TableLayout die gleichen Funktionen bietet wie ein komplexeres Layout mit vielen Positionsabhängigkeiten. Die Jetpack-Bibliothek ConstraintLayout bietet ähnliche Funktionen wie RelativeLayout sowie weitere Funktionen zum Erstellen von flachen, effizienteren Layouts.