Hardwarebeschleunigung

Ab Android 3.0 (API-Level 11) unterstützt die 2D-Rendering-Pipeline von Android die Hardwarebeschleunigung. Das bedeutet, dass alle Zeichenvorgänge, die auf dem Canvas einer View ausgeführt werden, die GPU verwenden. Ihre Anwendung verbraucht mehr RAM, da mehr Ressourcen für die Aktivierung der Hardwarebeschleunigung erforderlich sind.

Die Hardwarebeschleunigung ist standardmäßig aktiviert, wenn Ihr Ziel-API-Level >=14 ist, kann aber auch explizit aktiviert werden. Wenn Ihre Anwendung nur Standardansichten und Drawables verwendet, sollte die globale Aktivierung keine negativen Zeicheneffekte verursachen. Da die Hardwarebeschleunigung jedoch nicht für alle 2D-Zeichenvorgänge unterstützt wird, kann sich ihre Aktivierung auf einige Ihrer benutzerdefinierten Ansichten oder Zeichenaufrufe auswirken. Probleme treten normalerweise in Form von unsichtbaren Elementen, Ausnahmen oder falsch gerenderten Pixeln auf. Um dies zu beheben, bietet Android dir die Möglichkeit, die Hardwarebeschleunigung auf mehreren Ebenen zu aktivieren oder zu deaktivieren. Weitere Informationen finden Sie unter Hardwarebeschleunigung steuern.

Wenn Ihre Anwendung benutzerdefinierte Zeichnungen ausführt, testen Sie sie auf tatsächlichen Hardwaregeräten mit aktivierter Hardwarebeschleunigung, um Probleme zu finden. Im Abschnitt Unterstützung für Zeichenvorgänge werden bekannte Probleme mit der Hardwarebeschleunigung und entsprechende Umgehungen beschrieben.

Weitere Informationen finden Sie unter OpenGL mit den Framework APIs und Renderscript.

Hardwarebeschleunigung steuern

Sie können die Hardwarebeschleunigung auf folgenden Ebenen steuern:

  • Bewerbung
  • Aktivitäten
  • Fenster
  • Ansehen

Anwendungsebene

Füge in deiner Android-Manifestdatei das folgende Attribut in das Tag <application> ein, um die Hardwarebeschleunigung für deine gesamte App zu aktivieren:

<application android:hardwareAccelerated="true" ...>

Aktivitätslevel

Wenn sich Ihre Anwendung bei aktivierter Hardwarebeschleunigung nicht ordnungsgemäß verhält, können Sie sie auch für einzelne Aktivitäten steuern. Zum Aktivieren oder Deaktivieren der Hardwarebeschleunigung auf Aktivitätsebene können Sie das Attribut android:hardwareAccelerated für das Element <activity> verwenden. Im folgenden Beispiel wird die Hardwarebeschleunigung für die gesamte Anwendung aktiviert, aber für eine Aktivität deaktiviert:

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Fensterebene

Wenn Sie eine noch genauere Kontrolle benötigen, können Sie die Hardwarebeschleunigung für ein bestimmtes Fenster mit dem folgenden Code aktivieren:

Kotlin

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

Java

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

Hinweis: Die Hardwarebeschleunigung kann derzeit nicht auf Fensterebene deaktiviert werden.

Datenansichtsebene

Mit dem folgenden Code können Sie die Hardwarebeschleunigung für eine einzelne Ansicht zur Laufzeit deaktivieren:

Kotlin

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Java

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

Hinweis: Derzeit kann die Hardwarebeschleunigung nicht auf Ansichtsebene aktiviert werden. Ansichtsebenen haben neben der Deaktivierung der Hardwarebeschleunigung noch weitere Funktionen. Weitere Informationen zu ihrer Verwendung finden Sie unter Ebenen ansehen.

Bestimmen, ob eine Ansicht hardwarebeschleunigt ist

Manchmal ist es für eine Anwendung hilfreich, zu wissen, ob sie derzeit hardwarebeschleunigt ist, insbesondere bei benutzerdefinierten Ansichten. Dies ist besonders nützlich, wenn Ihre Anwendung viele benutzerdefinierte Zeichnungen ausführt und nicht alle Vorgänge von der neuen Rendering-Pipeline ordnungsgemäß unterstützt werden.

Es gibt zwei Möglichkeiten, um zu prüfen, ob eine Anwendung hardwarebeschleunigt ist:

Wenn du diese Überprüfung in deinem Zeichencode vornehmen musst, verwende nach Möglichkeit Canvas.isHardwareAccelerated() anstelle von View.isHardwareAccelerated(). Wenn eine Ansicht an ein hardwarebeschleunigtes Fenster angehängt ist, kann sie dennoch mit einem nicht hardwarebeschleunigten Canvas gezeichnet werden. Das ist beispielsweise der Fall, wenn eine Ansicht zu Caching-Zwecken in eine Bitmap gezeichnet wird.

Android-Zeichenmodelle

Wenn die Hardwarebeschleunigung aktiviert ist, verwendet das Android-Framework ein neues Zeichenmodell, das Anzeigelisten nutzt, um Ihre App auf dem Bildschirm zu rendern. Zum besseren Verständnis von Anzeigelisten und deren Auswirkungen auf Ihre Anwendung ist es hilfreich zu verstehen, wie Android Ansichten ohne Hardwarebeschleunigung erstellt. In den folgenden Abschnitten werden die softwarebasierten und hardwarebeschleunigten Zeichenmodelle beschrieben.

Softwarebasiertes Zeichenmodell

Im Software-Zeichenmodell werden Ansichten in den folgenden zwei Schritten gezeichnet:

  1. Hierarchie entkräften
  2. Hierarchie zeichnen

Immer wenn eine Anwendung einen Teil ihrer UI aktualisieren muss, ruft sie invalidate() (oder eine ihrer Varianten) in jeder Ansicht mit geändertem Inhalt auf. Die Entwertungsmeldungen werden bis nach oben in der Ansichtshierarchie weitergegeben, um die Regionen des Bildschirms zu berechnen, die neu gezeichnet werden müssen (der „schmutzige“ Bereich). Das Android-System zeichnet dann jede Ansicht in der Hierarchie, die sich mit der schmutzigen Region schneidet. Leider hat dieses Zeichenmodell zwei Nachteile:

  • Erstens erfordert dieses Modell die Ausführung von viel Code bei jedem Ziehdurchlauf. Wenn Ihre Anwendung beispielsweise invalidate() für eine Schaltfläche aufruft und sich diese Schaltfläche über einer anderen Ansicht befindet, zeichnet das Android-System die Ansicht neu, obwohl sie nicht geändert wurde.
  • Das zweite Problem besteht darin, dass das Zeichenmodell Fehler in Ihrer Anwendung ausblenden kann. Da das Android-System Ansichten neu zeichnet, wenn sie sich mit dem schmutzigen Bereich überschneiden, wird eine Ansicht, deren Inhalt Sie geändert haben, möglicherweise neu gezeichnet, obwohl invalidate() nicht dafür aufgerufen wurde. In diesem Fall müssen Sie darauf verlassen, dass eine andere Ansicht ungültig wird, um das richtige Verhalten zu erzielen. Dieses Verhalten kann sich bei jeder Änderung Ihrer Anwendung ändern. Aus diesem Grund sollten Sie für Ihre benutzerdefinierten Ansichten immer invalidate() aufrufen, wenn Sie Daten oder Status ändern, die sich auf den Zeichencode der Ansicht auswirken.

Hinweis: In Android-Ansichten wird automatisch invalidate() aufgerufen, wenn sich ihre Eigenschaften ändern, z. B. die Hintergrundfarbe oder der Text in einer TextView.

Hardwarebeschleunigtes Zeichnenmodell

Das Android-System verwendet weiterhin invalidate() und draw(), um Bildschirmaktualisierungen anzufordern und Ansichten zu rendern. Die eigentliche Zeichnung wird jedoch anders behandelt. Anstatt die Zeichenbefehle sofort auszuführen, werden sie vom Android-System in Anzeigelisten gespeichert, die die Ausgabe des Zeichencodes der Ansichtshierarchie enthalten. Eine weitere Optimierung besteht darin, dass das Android-System nur Anzeigelisten für Aufrufe aufzeichnen und aktualisieren muss, die durch einen invalidate()-Aufruf als „schmutzig“ gekennzeichnet wurden. Ansichten, die nicht ungültig gemacht wurden, können einfach neu gezeichnet werden, indem die zuvor aufgezeichnete Anzeigeliste neu ausgegeben wird. Das neue Zeichnungsmodell besteht aus drei Phasen:

  1. Hierarchie entkräften
  2. Anzeigelisten aufzeichnen und aktualisieren
  3. Anzeigelisten zeichnen

Bei diesem Modell können Sie sich nicht auf eine Ansicht verlassen, die die schmutzige Region überschneidet, um ihre draw()-Methode auszuführen. Damit das Android-System die Anzeigeliste einer Ansicht aufzeichnet, musst du invalidate() aufrufen. Andernfalls sieht eine Ansicht auch nach der Änderung gleich aus.

Die Verwendung von Displaylisten verbessert auch die Animationsleistung, da beim Festlegen bestimmter Eigenschaften wie Alpha oder Rotation die Zielansicht nicht ungültig sein muss (dies erfolgt automatisch). Diese Optimierung gilt auch für Datenansichten mit Displaylisten, d. h. für alle Ansichten mit hardwarebeschleunigter Anwendung. Angenommen, es gibt eine LinearLayout, die einen ListView über einem Button enthält. Die Anzeigeliste für LinearLayout sieht so aus:

  • DrawDisplayList(Listenansicht)
  • DrawDisplayList(Schaltfläche)

Angenommen, Sie möchten die Deckkraft von ListView ändern. Nach dem Aufruf von setAlpha(0.5f) für ListView enthält die Anzeigeliste jetzt Folgendes:

  • SaveLayerAlpha(0,5)
  • DrawDisplayList(Listenansicht)
  • Wiederherstellen
  • DrawDisplayList(Schaltfläche)

Der komplexe Zeichencode von ListView wurde nicht ausgeführt. Stattdessen wurde nur die Anzeigeliste der viel einfacheren LinearLayout aktualisiert. In einer Anwendung ohne aktivierte Hardwarebeschleunigung wird der Zeichencode der Liste und der übergeordneten Liste noch einmal ausgeführt.

Unterstützung für Zeichenvorgänge

Mit Hardwarebeschleunigung unterstützt die 2D-Rendering-Pipeline die am häufigsten verwendeten Canvas-Zeichenvorgänge sowie viele weniger genutzte Vorgänge. Es werden alle Zeichenvorgänge unterstützt, die zum Rendern von Apps, die mit Android ausgeliefert werden, Standard-Widgets und -Layouts sowie häufig verwendete visuelle Effekte wie Reflexionen und gekachelte Texturen.

In der folgenden Tabelle wird die Supportstufe für verschiedene Vorgänge auf den verschiedenen API-Ebenen beschrieben:

Erstes unterstütztes API-Level
Canvas
printBitmapMesh() (Farbarray) 18
DrawPicture() 23
DrawPosText() 16
printTextOnPath() 16
printVertices() 29
SetDrawFilter() 16
ClipPath() 18
ClipRegion() 18
ClipRect(Region.Op.XOR) 18
ClipRect(Region.Op.Difference) 18
ClipRect(Region.Op.ReverseDifference) 18
ClipRect() mit Drehung/Perspektive 18
Farbe
setAntiAlias() (für Text) 18
setAntiAlias() (für Zeilen) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (für Linien) 28
setShadowLayer() (außer Text) 28
setStrokeCap() (für Zeilen) 18
setStrokeCap() (für Punkte) 19
setSubpixelText() 28
Xfer-Modus
PorterDuff.Mode.DARKEN (Framebuffer) 28
PorterDuff.Mode.LIGHTEN (Framebuffer) 28
PorterDuff.Mode.OVERLAY (Framebuffer) 28
Shader
ComposeShader in ComposeShader 28
Shader desselben Typs in ComposeShader 28
Lokale Matrix in ComposeShader 18

Canvas-Skalierung

Die hardwarebeschleunigte 2D-Rendering-Pipeline wurde zuerst für das nicht skalierte Zeichnen entwickelt, wobei einige Zeichenvorgänge die Qualität bei höheren Skalenwerten erheblich verschlechtern. Diese Operationen werden als Texturen implementiert, die im Maßstab 1,0 gezeichnet und von der GPU transformiert werden. Ab API-Level 28 können alle Zeichenvorgänge problemlos skaliert werden.

In der folgenden Tabelle sehen Sie, wann die Implementierung geändert wurde, damit große Datenmengen korrekt verarbeitet werden konnten:
Zu skalierender Zeichenvorgang Erstes unterstütztes API-Level
DrawText() 18
DrawPosText() 28
printTextOnPath() 28
Einfache Formen* 17
Komplexe Formen* 28
DrawPath() 28
Schattenebene 28

Hinweis: „Einfache Formen“ sind die Befehle drawRect(), drawCircle(), drawOval(), drawRoundRect() und drawArc() (mit useCenter=false), die mit einem Paint ausgeführt werden, der keinen Pfadeffekt hat und keine nicht standardmäßigen Joins über setStrokeJoin()/setStrokeMiter() enthält. Andere Instanzen dieser Zeichenbefehle fallen im obigen Diagramm unter „Komplex“.

Wenn Ihre Anwendung von einer dieser Funktionen oder Einschränkungen betroffen ist, können Sie die Hardwarebeschleunigung nur für den betroffenen Teil Ihrer Anwendung deaktivieren. Rufen Sie dazu setLayerType(View.LAYER_TYPE_SOFTWARE, null) auf. So können Sie die Hardwarebeschleunigung auch an anderen Orten nutzen. Weitere Informationen zum Aktivieren und Deaktivieren der Hardwarebeschleunigung auf verschiedenen Ebenen in Ihrer Anwendung finden Sie unter Hardwarebeschleunigung steuern.

Ebenen ansehen

In allen Android-Versionen konnten Ansichten in Zwischenspeicher außerhalb des Bildschirms gerendert werden, entweder mithilfe des Zeichen-Cache einer Ansicht oder mithilfe von Canvas.saveLayer(). Für Off-Screen-Zwischenspeicher (sogenannte Layers) gibt es mehrere Verwendungszwecke. Sie können sie verwenden, um eine bessere Leistung bei der Animation komplexer Ansichten oder beim Anwenden von Kompositionseffekten zu erzielen. Du kannst beispielsweise Überblendungseffekte mit Canvas.saveLayer() implementieren, um eine Ansicht vorübergehend in einer Ebene zu rendern und sie dann mit einem Deckkraftfaktor wieder auf dem Bildschirm zu platzieren.

Ab Android 3.0 (API-Level 11) hast du mit der Methode View.setLayerType() mehr Kontrolle darüber, wie und wann Ebenen verwendet werden. Diese API verwendet zwei Parameter: den Typ der Ebene, die Sie verwenden möchten, und ein optionales Paint-Objekt, das beschreibt, wie die Ebene zusammengesetzt werden soll. Mit dem Parameter Paint können Sie Farbfilter, spezielle Mischmodi oder Deckkraft auf eine Ebene anwenden. Eine Ansicht kann einen von drei Ebenentypen verwenden:

  • LAYER_TYPE_NONE: Die Ansicht wird normal gerendert und nicht durch einen Zwischenspeicher außerhalb des Bildschirms gestützt. Das ist die Standardeinstellung.
  • LAYER_TYPE_HARDWARE: Wenn die Anwendung hardwarebeschleunigt ist, wird die Ansicht in der Hardware in eine Hardwaretextur gerendert. Wenn die Anwendung nicht hardwarebeschleunigt ist, verhält sich dieser Ebenentyp wie LAYER_TYPE_SOFTWARE.
  • LAYER_TYPE_SOFTWARE: Die Ansicht wird in Software als Bitmap gerendert.

Welche Art von Ebene Sie verwenden, hängt von Ihrem Ziel ab:

  • Leistung: Verwenden Sie einen Hardwareebenentyp, um eine Ansicht als Hardware-Textur zu rendern. Nachdem eine Ansicht in einer Ebene gerendert wurde, muss ihr Zeichencode erst ausgeführt werden, wenn die Ansicht invalidate() aufruft. Einige Animationen, z. B. Alpha-Animationen, können dann direkt auf die Ebene angewendet werden, was für die GPU sehr effizient ist.
  • Visuelle Effekte: Verwenden Sie einen Hardware- oder Softwareebenentyp und eine Paint, um einer Ansicht besondere visuelle Effekte hinzuzufügen. Beispielsweise können Sie mit ColorMatrixColorFilter eine Ansicht in Schwarz-Weiß zeichnen.
  • Kompatibilität: Verwende einen Softwareebenentyp, um das Rendern einer Ansicht in Software zu erzwingen. Wenn bei einer hardwarebeschleunigten Ansicht (z. B. wenn Ihre gesamte Anwendung hardwarebeschleunigt) Renderingprobleme hat, können Sie mit dieser Methode die Einschränkungen der Hardware-Rendering-Pipeline einfach umgehen.

Ebenen und Animationen ansehen

Hardwareebenen können schnellere und flüssigere Animationen liefern, wenn Ihre Anwendung hardwarebeschleunigt ist. Das Ausführen einer Animation mit 60 Bildern pro Sekunde ist nicht immer möglich, wenn komplexe Ansichten animiert werden, die viele Zeichenvorgänge erfordern. Dies kann abgemildert werden, indem Hardwareebenen verwendet werden, um die Ansicht als Hardware-Textur zu rendern. Die Hardware-Textur kann dann zum Animieren der Ansicht verwendet werden, sodass die Ansicht sich während der Animation nicht ständig neu zeichnen muss. Die Ansicht wird nur neu gezeichnet, wenn Sie die Eigenschaften der Ansicht ändern, wodurch invalidate() aufgerufen wird, oder wenn Sie invalidate() manuell aufrufen. Wenn Sie eine Animation in Ihrer Anwendung ausführen und nicht die gewünschten reibungslosen Ergebnisse erzielen, sollten Sie eventuell Hardwareebenen für Ihre animierten Ansichten aktivieren.

Wenn eine Ansicht von einer Hardwareebene unterstützt wird, werden einige ihrer Eigenschaften dadurch gehandhabt, wie die Ebene auf dem Bildschirm zusammengesetzt ist. Das Festlegen dieser Attribute ist effizient, da die Ansicht nicht ungültig gemacht und neu gezeichnet werden muss. Die folgende Liste von Eigenschaften beeinflusst die Art und Weise, wie die Ebene zusammengesetzt ist. Wenn der Setter für eines dieser Attribute aufgerufen wird, erfolgt eine optimale Entwertung und kein Neuzeichnen der Zielansicht:

  • alpha: Ändert die Deckkraft der Ebene
  • x, y, translationX, translationY: Ändert die Position der Ebene
  • scaleX, scaleY: Ändert die Größe der Ebene
  • rotation, rotationX, rotationY: ändert die Ausrichtung der Ebene im 3D-Raum
  • pivotX, pivotY: Ändert den Transformationsursprung der Ebene

Diese Properties sind die Namen, die beim Animieren einer Ansicht mit einem ObjectAnimator verwendet werden. Wenn Sie auf diese Eigenschaften zugreifen möchten, rufen Sie den entsprechenden Setter oder Getter auf. Wenn Sie beispielsweise die Alpha-Eigenschaft ändern möchten, rufen Sie setAlpha() auf. Das folgende Code-Snippet zeigt die effizienteste Methode, um eine 3D-Ansicht um die Y-Achse zu drehen:

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).start()

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

Da Hardwareebenen Videospeicher verbrauchen, wird dringend empfohlen, sie nur für die Dauer der Animation zu aktivieren und nach der Animation wieder zu deaktivieren. Dies erreichen Sie mithilfe von Animations-Listenern:

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).apply {
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            view.setLayerType(View.LAYER_TYPE_NONE, null)
        }
    })
    start()
}

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

Weitere Informationen zur Property-Animation finden Sie unter Property-Animation.

Tipps und Tricks

Der Wechsel zu hardwarebeschleunigter 2D-Grafik kann die Leistung sofort steigern. Sie sollten Ihre Anwendung dennoch so konzipieren, dass die GPU effektiv genutzt wird. Beachten Sie dabei die folgenden Empfehlungen:

Anzahl der Aufrufe in Ihrer Anwendung reduzieren
Je mehr Ansichten das System zum Zeichnen hat, desto langsamer wird es. Dies gilt auch für die Rendering-Pipeline der Software. Das Reduzieren von Ansichten ist eine der einfachsten Möglichkeiten, Ihre Benutzeroberfläche zu optimieren.
Überschneidung vermeiden
Zeichnen Sie nicht zu viele Ebenen übereinander. Entfernen Sie alle Ansichten, die vollständig durch andere undurchsichtige Ansichten darüber verdeckt werden. Wenn du mehrere übereinanderliegende Ebenen zeichnen möchtest, kannst du sie zu einer einzigen Ebene zusammenführen. Bei der aktuellen Hardware gilt als Faustregel: Zeichnen Sie nicht mehr als das 2,5-Fache der Anzahl der Pixel auf dem Bildschirm pro Frame (transparente Pixel in einer Bitmap-Anzahl!).
Keine Renderingobjekte in „Drehmethoden“ erstellen
Ein häufiger Fehler besteht darin, bei jedem Aufruf einer Renderingmethode eine neue Paint oder eine neue Path zu erstellen. Dadurch wird die automatische Speicherbereinigung häufiger ausgeführt und Caches und Optimierungen in der Hardwarepipeline werden umgangen.
Formen nicht zu oft ändern
Komplexe Formen, Pfade und Kreise werden beispielsweise mithilfe von Texturmasken gerendert. Jedes Mal, wenn Sie einen Pfad erstellen oder ändern, erstellt die Hardwarepipeline eine neue Maske, was teuer werden kann.
Bitmaps nicht zu oft ändern
Jedes Mal, wenn Sie den Inhalt einer Bitmap ändern, wird sie beim nächsten Zeichnen noch einmal als GPU-Textur hochgeladen.
Alphaversion mit Vorsicht verwenden
Wenn Sie eine Ansicht mit setAlpha(), AlphaAnimation oder ObjectAnimator durchscheinen lassen, wird sie außerhalb des Bildschirms gerendert, wodurch die erforderliche Ausführungsrate verdoppelt wird. Wenn Sie Alpha auf sehr große Ansichten anwenden, sollten Sie den Ebenentyp der Ansicht auf LAYER_TYPE_HARDWARE setzen.