In diesem Dokument erfahren Sie, wie Sie wichtige Leistungsprobleme in Ihrer App erkennen und beheben.
Wichtige Leistungsprobleme
Es gibt viele Probleme, die zu einer schlechten Leistung einer App beitragen können. Im Folgenden finden Sie einige häufige Probleme, die Sie in Ihrer App prüfen sollten:
- Startlatenz
Die Startverzögerung ist die Zeitspanne zwischen dem Tippen auf das App-Symbol, die Benachrichtigung oder einen anderen Einstiegspunkt und dem Einblenden der Daten des Nutzers auf dem Bildschirm.
Für Ihre Apps sollten Sie die folgenden Startziele anstreben:
Kaltstart in weniger als 500 ms. Ein Kaltstart erfolgt, wenn die gestartete App nicht im Arbeitsspeicher des Systems vorhanden ist. Das ist der Fall, wenn die App zum ersten Mal seit dem Neustart oder seit dem Beenden des App-Prozesses durch den Nutzer oder das System gestartet wird.
Ein Warmstart hingegen erfolgt, wenn die App bereits im Hintergrund ausgeführt wird. Ein Kaltstart erfordert die meiste Arbeit des Systems, da es alles aus dem Speicher laden und die App initialisieren muss. Versuchen Sie, Kaltstarts auf 500 ms oder weniger zu begrenzen.
P95- und P99-Latenzen liegen sehr nahe an der Medianlatenz. Wenn die App lange zum Starten braucht, ist das für die Nutzer frustrierend. Interprozesskommunikation (IPC) und unnötige E/A-Vorgänge während des kritischen Pfads beim Starten der App können zu Sperrkonflikten und Inkonsistenzen führen.
- Ruckeln beim Scrollen
Jank ist der Begriff für das visuelle Ruckeln, das auftritt, wenn das System nicht in der Lage ist, Frames rechtzeitig zu erstellen und bereitzustellen, um sie mit der angeforderten Taktfrequenz von 60 Hz oder höher auf dem Bildschirm darzustellen. Ruckler sind am deutlichsten beim Scrollen zu sehen, wenn statt einer flüssigen Animation Ruckler auftreten. Ruckler treten auf, wenn die Bewegung für einen oder mehrere Frames pausiert, da das Rendern von Inhalten in der App länger dauert als die Dauer eines Frames auf dem System.
Apps müssen auf eine Aktualisierungsrate von 90 Hz ausgerichtet sein. Herkömmliche Renderingraten betragen 60 Hz. Viele neuere Geräte arbeiten jedoch bei Nutzerinteraktionen wie dem Scrollen im 90 Hz-Modus. Einige Geräte unterstützen sogar noch höhere Bildwiederholraten von bis zu 120 Hz.
Wenn Sie sehen möchten, welche Bildwiederholrate auf einem Gerät gerade verwendet wird, aktivieren Sie im Bereich Debugging die Option Entwickleroptionen > Bildwiederholrate anzeigen.
- Ruckelige Übergänge
Das ist bei Interaktionen wie dem Wechseln zwischen Tabs oder dem Laden einer neuen Aktivität zu sehen. Diese Arten von Übergängen müssen flüssige Animationen sein und dürfen keine Verzögerungen oder visuelles Flimmern enthalten.
- Ineffiziente Energienutzung
Durch die Arbeit wird der Akku entladen und unnötige Arbeit verkürzt die Akkulaufzeit.
Die Speicherzuweisung, die durch das Erstellen neuer Objekte im Code verursacht wird, kann zu erheblichen Arbeitslasten im System führen. Das liegt daran, dass nicht nur die Zuweisungen selbst von der Android Runtime (ART) beansprucht werden, sondern auch das spätere Freigeben dieser Objekte (Garbage Collection) Zeit und Aufwand erfordert. Sowohl die Zuweisung als auch die Sammlung sind viel schneller und effizienter, insbesondere bei temporären Objekten. Früher war es eine Best Practice, Objekte nach Möglichkeit nicht zuzuweisen. Wir empfehlen jedoch, das zu tun, was für Ihre App und Architektur am sinnvollsten ist. Bei ART ist es nicht empfehlenswert, bei der Zuweisung zu sparen, wenn dadurch der Code nicht mehr wartbar ist.
Das erfordert jedoch Aufwand. Denken Sie daran, dass es zu Leistungsproblemen beitragen kann, wenn Sie viele Objekte in Ihrem inneren Loop zuweisen.
Probleme identifizieren
Wir empfehlen den folgenden Workflow, um Leistungsprobleme zu identifizieren und zu beheben:
- Identifizieren und prüfen Sie die folgenden wichtigen User Journeys:
- Gängige Startabläufe, einschließlich Launcher und Benachrichtigung
- Bildschirme, auf denen Nutzer durch Daten scrollen.
- Übergänge zwischen Bildschirmen
- Langlaufende Abläufe wie Navigation oder Musikwiedergabe
- Mit den folgenden Debugging-Tools können Sie prüfen, was während der vorherigen Aufrufe passiert:
- Perfetto: Mit diesem Tool können Sie mithilfe präziser Zeitdaten sehen, was auf dem gesamten Gerät passiert.
- Memory Profiler: Hier sehen Sie, welche Arbeitsspeicherzuordnungen im Heap stattfinden.
- Simpleperf: Zeigt ein Flame-Diagramm dazu an, welche Funktionsaufrufe in einem bestimmten Zeitraum die meiste CPU beanspruchen. Wenn Sie in Systrace etwas finden, das viel Zeit in Anspruch nimmt, aber nicht wissen, warum, kann Simpleperf zusätzliche Informationen liefern.
Um diese Leistungsprobleme zu verstehen und zu beheben, ist es wichtig, einzelne Testläufe manuell zu debuggen. Die vorherigen Schritte können nicht durch die Analyse aggregierter Daten ersetzt werden. Um jedoch zu verstehen, was Nutzer tatsächlich sehen und zu erkennen, wann es zu Rückschritten kommen kann, ist es wichtig, die Erfassung von Messwerten in automatisierten Tests und im Feld einzurichten:
- Startvorgänge
- Feldmesswerte: Startzeit der Play Console
- Lab-Tests: Start mit Macrobenchmark testen
- Jank
- Feldmesswerte
- Play Console-Frame-Vitals: In der Play Console können Sie Messwerte nicht auf einen bestimmten User Journey eingrenzen. Es wird nur die allgemeine Rucklerrate in der App erfasst.
- Benutzerdefinierte Messung mit
FrameMetricsAggregator
: MitFrameMetricsAggregator
können Sie Jank-Messwerte während eines bestimmten Workflows erfassen.
- Labortests
- Scrollen mit Macrobenchmark
- Macrobenchmark erfasst das Frame-Timing mit
dumpsys gfxinfo
-Befehlen, die eine einzelne User Journey umfassen. So können Sie die Schwankungen der Ruckler während eines bestimmten Nutzerpfads nachvollziehen. DieRenderTime
-Messwerte, die angeben, wie lange das Zeichnen von Frames dauert, sind wichtiger als die Anzahl der ruckeligen Frames, um Rückschritte oder Verbesserungen zu erkennen.
- Feldmesswerte
Probleme bei der Bestätigung von App-Links
App-Links sind Deeplinks, die auf Ihrer Website-URL basieren und nachweislich zu Ihrer Website gehören. Im Folgenden sind einige Gründe aufgeführt, die zu fehlgeschlagenen App-Link-Bestätigungen führen können.
- Intent-Filterbereiche: Fügen Sie
autoVerify
nur Intent-Filtern für URLs hinzu, auf die Ihre App reagieren kann. - Nicht bestätigte Protokollübergänge: Nicht bestätigte serverseitige und Subdomain-Weiterleitungen gelten als Sicherheitsrisiken und werden nicht bestätigt. Dadurch schlagen alle
autoVerify
-Links fehl. Wenn Sie beispielsweise Links von HTTP zu HTTPS weiterleiten, z. B. von beispiel.de zu www.beispiel.de, ohne die HTTPS-Links zu überprüfen, kann dies zu einer fehlgeschlagenen Überprüfung führen. Überprüfen Sie App-Links, indem Sie Intent-Filter hinzufügen. - Nicht überprüfbare Links: Wenn Sie zu Testzwecken nicht überprüfbare Links hinzufügen, werden die App-Links für Ihre App möglicherweise nicht vom System überprüft.
- Unzuverlässige Server: Achten Sie darauf, dass Ihre Server eine Verbindung zu Ihren Client-Apps herstellen können.
App für Leistungsanalyse einrichten
Eine korrekte Einrichtung ist wichtig, um genaue, wiederholbare und umsetzbare Benchmarks für eine App zu erhalten. Testen Sie auf einem System, das der Produktionsumgebung so nahe wie möglich kommt, und unterdrücken Sie Störquellen. In den folgenden Abschnitten werden einige APK- und systemspezifische Schritte zur Vorbereitung einer Testeinrichtung beschrieben, von denen einige nutzungsfallspezifisch sind.
Tracepoints
Apps können ihren Code mit benutzerdefinierten Trace-Ereignissen instrumentieren.
Während der Erfassung von Traces verursacht das Tracing einen kleinen Overhead von etwa 5 µs pro Abschnitt. Verwenden Sie es also nicht für jede Methode. Wenn Sie größere Arbeitspakete mit einer Dauer von mehr als 0,1 Millisekunden erfassen, können Sie wichtige Erkenntnisse zu Engpässen gewinnen.
Hinweise zu APKs
Debug-Varianten können bei der Fehlerbehebung und Symbolisierung von Stack-Samples hilfreich sein, haben aber schwerwiegende Auswirkungen auf die Leistung. Auf Geräten mit Android 10 (API-Level 29) und höher kann profileable android:shell="true"
im Manifest verwendet werden, um das Profiling in Release-Builds zu aktivieren.
Verwenden Sie die Konfiguration für die Code-Komprimierung in Produktionsqualität. Je nach den von Ihrer App verwendeten Ressourcen kann sich das erheblich auf die Leistung auswirken. In einigen ProGuard-Konfigurationen werden Tracepoints entfernt. Entfernen Sie diese Regeln daher für die Konfiguration, für die Sie Tests ausführen.
Compilation
Kompilieren Sie Ihre App auf dem Gerät in einen bekannten Zustand – in der Regel speed
für mehr Einfachheit oder speed-profile
für eine bessere Übereinstimmung mit der Produktionsleistung. Dies erfordert jedoch das Aufwärmen der Anwendung und das Dumpen von Profilen oder das Kompilieren der Baseline-Profile der App.
Sowohl speed
als auch speed-profile
reduzieren die Menge des aus dex interpretierten Codes und damit die Menge der Just-in-Time-Kompilierung (JIT) im Hintergrund, die zu erheblichen Störungen führen kann. Nur speed-profile
verringert die Auswirkungen des Ladens von Klassen zur Laufzeit aus dex.
Mit dem folgenden Befehl wird die Anwendung im speed
-Modus kompiliert:
adb shell cmd package compile -m speed -f com.example.packagename
Im speed
-Kompilierungsmodus werden die Methoden der App vollständig kompiliert. Im Modus speed-profile
werden die Methoden und Klassen der App gemäß einem Profil der verwendeten Codepfade kompiliert, das während der App-Nutzung erfasst wird. Es kann schwierig sein, Profile einheitlich und korrekt zu erheben. Wenn Sie sich also dafür entscheiden, sie zu verwenden, prüfen Sie, ob die Daten wie erwartet erhoben werden. Die Profile befinden sich an folgendem Speicherort:
/data/misc/profiles/ref/[package-name]/primary.prof
Systemanforderungen
Für Low-Level- und High-Fidelity-Messungen müssen Sie Ihre Geräte kalibrieren. Führen Sie A/B-Vergleiche auf demselben Gerät und mit derselben Betriebssystemversion aus. Die Leistung kann selbst bei Geräten desselben Typs erheblich variieren.
Auf gerooteten Geräten können Sie für Mikrobenchmarks ein lockClocks
-Script verwenden. Diese Scripts führen unter anderem folgende Aufgaben aus:
- CPUs auf eine feste Taktfrequenz stellen.
- Deaktivieren Sie kleine Kerne und konfigurieren Sie die GPU.
- Deaktivieren Sie die thermische Drosselung.
Wir empfehlen nicht, ein lockClocks
-Script für Tests zur Nutzerfreundlichkeit wie App-Start, DoU-Tests und Ruckeltests zu verwenden. Es kann jedoch unerlässlich sein, um Rauschen in Mikrobenchmark-Tests zu reduzieren.
Verwenden Sie nach Möglichkeit ein Test-Framework wie Macrobenchmark, um Abweichungen bei Ihren Messungen zu reduzieren und Messfehler zu vermeiden.
Langsamer App-Start: Unnötige Trampolinaktivität
Eine Trampolinaktivität kann die App-Startzeit unnötig verlängern. Sie sollten daher wissen, ob das bei Ihrer App der Fall ist. Wie im folgenden Beispiel-Trace zu sehen, folgt auf eine activityStart
sofort eine weitere activityStart
, ohne dass durch die erste Aktivität Frames gezeichnet werden.
Das kann sowohl am Einstiegspunkt einer Benachrichtigung als auch am Einstiegspunkt eines regulären App-Starts passieren. Sie können das Problem oft durch Refactoring beheben. Wenn Sie diese Aktivität beispielsweise verwenden, um die Einrichtung vor dem Ausführen einer anderen Aktivität durchzuführen, sollten Sie diesen Code in eine wiederverwendbare Komponente oder Bibliothek einbinden.
Unnötige Zuordnungen, die häufige GCs auslösen
In einem Systrace sehen Sie möglicherweise, dass die automatische Speicherbereinigung häufiger als erwartet erfolgt.
Im folgenden Beispiel ist ein Wert alle 10 Sekunden während eines langwierigen Vorgangs ein Indikator dafür, dass die App möglicherweise unnötig, aber kontinuierlich Speicher zuweist:
Möglicherweise stellen Sie auch fest, dass ein bestimmter Aufrufstapel den Großteil der Zuweisungen ausführt, wenn Sie den Speicher-Profiler verwenden. Sie müssen nicht alle Zuweisungen radikal entfernen, da dies die Wartung des Codes erschweren kann. Konzentrieren Sie sich stattdessen auf Hotspots der Zuweisungen.
Janky-Frames
Die Grafikpipeline ist relativ kompliziert und es kann etwas schwierig sein zu bestimmen, ob ein Nutzer letztendlich einen Frame-Drop sehen könnte. In einigen Fällen kann die Plattform einen Frame mithilfe von Puffern „retten“. Sie können diese Nuancen jedoch größtenteils ignorieren, um problematische Frames aus der Perspektive Ihrer App zu identifizieren.
Wenn Frames mit wenig Aufwand von der App gerendert werden, werden die Choreographer.doFrame()
-Tracepoints auf einem Gerät mit 60 fps alle 16,7 ms erfasst:
Wenn Sie herauszoomen und sich durch den Trace bewegen, sehen Sie manchmal, dass Frames etwas länger dauern.Das ist aber in Ordnung, da sie nicht länger als die zugewiesenen 16,7 ms dauern:
Wenn diese regelmäßige Kadenz unterbrochen wird, ist das ein ruckelnder Frame, wie in Abbildung 5 dargestellt:
Sie können üben, sie zu erkennen.
In einigen Fällen müssen Sie einen Tracepoint heranzoomen, um mehr darüber zu erfahren, welche Ansichten aufgebläht werden oder was RecyclerView
tut. In anderen Fällen müssen Sie möglicherweise weitere Untersuchungen durchführen.
Weitere Informationen zum Identifizieren von Rucklern und zum Beheben der Ursachen findest du unter Langsames Rendern.
Häufige Fehler bei RecyclerView
Wenn die gesamten Sicherungsdaten von RecyclerView
unnötigerweise ungültig gemacht werden, kann das zu langen Frame-Rendering-Zeiten und Rucklern führen. Um die Anzahl der Ansichten zu minimieren, die aktualisiert werden müssen, sollten Sie stattdessen nur die Daten ungültig machen, die sich ändern.
Unter Dynamische Daten präsentieren finden Sie Informationen dazu, wie Sie teure notifyDatasetChanged()
-Aufrufe vermeiden, die dazu führen, dass Inhalte aktualisiert werden, anstatt sie vollständig zu ersetzen.
Wenn Sie nicht alle verschachtelten RecyclerView
-Elemente ordnungsgemäß unterstützen, kann es dazu führen, dass die interne RecyclerView
jedes Mal vollständig neu erstellt wird. Für jedes verschachtelte innere RecyclerView
muss ein RecycledViewPool
festgelegt sein, damit Ansichten zwischen allen inneren RecyclerView
wiederverwendet werden können.
Wenn nicht genügend Daten oder nicht rechtzeitig vorab abgerufen werden, kann es beim Erreichen des Endes einer scrollbaren Liste zu Rucklern kommen, wenn ein Nutzer auf weitere Daten vom Server warten muss. Technisch gesehen ist das zwar kein Ruckeln, da keine Frame-Deadlines verpasst werden, aber Sie können die UX erheblich verbessern, indem Sie das Timing und die Menge des Prefetching ändern, damit der Nutzer nicht auf Daten warten muss.
App-Fehler beheben
Im Folgenden finden Sie verschiedene Methoden zur Fehlerbehebung bei der Leistung Ihrer App. Im folgenden Video erhalten Sie einen Überblick über das System-Tracing und die Verwendung des Android Studio-Profilers.
App-Start mit Systrace debuggen
Unter App-Startzeit finden Sie eine Übersicht über den App-Startvorgang. Im folgenden Video erhalten Sie eine Übersicht über das System-Tracing.
Sie können die Starttypen in den folgenden Phasen unterscheiden:
- Kaltstart: Es wird ein neuer Prozess ohne gespeicherten Status erstellt.
- Warmstart: Die Aktivität wird entweder neu erstellt, während der Prozess wiederverwendet wird, oder der Prozess wird mit dem gespeicherten Status neu erstellt.
- Hot-Start: Die Aktivität wird neu gestartet und beginnt bei der Inflation.
Wir empfehlen, Systraces mit der System Tracing App auf dem Gerät zu erfassen. Für Android 10 und höher verwenden Sie Perfetto. Bei Android 9 und niedriger verwenden Sie Systrace. Wir empfehlen außerdem, Trace-Dateien mit dem webbasierten Perfetto-Trace-Viewer anzusehen. Weitere Informationen finden Sie unter Übersicht über das System-Tracing.
Achten Sie unter anderem auf Folgendes:
- Monitorkonflikt: Der Kampf um vom Monitor geschützte Ressourcen kann zu erheblichen Verzögerungen beim Starten der App führen.
Synchrone Binder-Transaktionen: Suchen Sie nach unnötigen Transaktionen im kritischen Pfad Ihrer App. Wenn eine erforderliche Transaktion teuer ist, sollten Sie mit dem zugehörigen Plattformteam zusammenarbeiten, um Verbesserungen vorzunehmen.
Gleichzeitiger GC: Dies ist häufig und hat relativ geringe Auswirkungen. Wenn es jedoch häufig auftritt, sollten Sie es mit dem Android Studio-Speicher-Profiler untersuchen.
E/A: Prüfen Sie, ob beim Start E/A-Vorgänge ausgeführt werden, und achten Sie auf lange Aussetzer.
Intensive Aktivitäten in anderen Threads: Diese können den UI-Thread beeinträchtigen. Achten Sie daher beim Starten auf Hintergrundaktivitäten.
Wir empfehlen, reportFullyDrawn
aufzurufen, wenn der Start aus Sicht der App abgeschlossen ist, um Berichte zu den Startmesswerten der App zu verbessern. Weitere Informationen zur Verwendung von reportFullyDrawn
finden Sie im Abschnitt Zeit bis zur vollständigen Anzeige.
Sie können über den Perfetto-Trace-Prozessor von RFD definierte Startzeiten extrahieren. Daraufhin wird ein für Nutzer sichtbares Trace-Ereignis ausgegeben.
System-Tracing auf dem Gerät verwenden
Mit der App „System-Tracing“ auf Systemebene können Sie einen System-Trace auf einem Gerät erfassen. Mit dieser App können Sie Traces vom Gerät aufzeichnen, ohne es anschließen oder mit adb
verbinden zu müssen.
Memory Profiler von Android Studio verwenden
Mit dem Memory Profiler von Android Studio können Sie den Arbeitsspeicherdruck prüfen, der durch Speicherlecks oder schlechte Nutzungsmuster verursacht werden kann. Sie bietet eine Liveansicht der Objektzuweisungen.
Sie können Speicherprobleme in Ihrer App beheben, indem Sie anhand der Informationen aus dem Speicher-Profiler nachverfolgen, warum und wie oft GCs auftreten.
So erstellen Sie ein App-Speicherprofil:
Arbeitsspeicherprobleme erkennen
Erstellen Sie eine Sitzung zum Erstellen eines Arbeitsspeicherprofils für die User Journey, auf die Sie sich konzentrieren möchten. Achten Sie auf eine zunehmende Objektanzahl, wie in Abbildung 7 dargestellt, die schließlich zu GCs führt, wie in Abbildung 8 dargestellt.
Nachdem Sie die User Journey ermittelt haben, die den Speicherdruck erhöht, suchen Sie nach den Ursachen des Speicherdrucks.
Hotspots für Arbeitsspeicherdruck diagnostizieren
Wählen Sie einen Bereich in der Zeitachse aus, um sowohl Zuweisungen als auch Größe der flachen Struktur zu visualisieren, wie in Abbildung 9 dargestellt.
Es gibt mehrere Möglichkeiten, diese Daten zu sortieren. Im Folgenden finden Sie einige Beispiele dafür, wie Sie mit den einzelnen Ansichten Probleme analysieren können.
Nach Klasse sortieren: Hilfreich, wenn Sie Klassen finden möchten, die Objekte generieren, die andernfalls im Cache gespeichert oder aus einem Speicherpool wiederverwendet werden.
Wenn eine App beispielsweise jede Sekunde 2.000 Objekte der Klasse „Vertex“ erstellt, erhöht sich die Anzahl der Zuweisungen jede Sekunde um 2.000. Dieser Wert wird angezeigt, wenn Sie nach Klasse sortieren. Wenn Sie diese Objekte wiederverwenden möchten, um das Generieren von Junk-Dateien zu vermeiden, implementieren Sie einen Speicherpool.
Nach Aufrufstack sortieren: Nützlich, wenn Sie einen Hotpath finden möchten, in dem Speicher zugewiesen wird, z. B. in einer Schleife oder in einer bestimmten Funktion, die viel Zuweisungsarbeit ausführt.
Shallow Size: Es wird nur der Arbeitsspeicher des Objekts selbst erfasst. Sie eignet sich zum Erfassen einfacher Klassen, die hauptsächlich aus primitiven Werten bestehen.
Beibehaltene Größe: Der Gesamtspeicherplatz, der durch das Objekt und die Verweise belegt wird, auf die nur über das Objekt verwiesen wird. Es ist nützlich, um den Speicherdruck aufgrund komplexer Objekte zu überwachen. Um diesen Wert zu erhalten, erstellen Sie einen vollständigen Speicherdump (siehe Abbildung 10) und fügen Sie die Spalte Retained Size hinzu (siehe Abbildung 11).
Auswirkungen einer Optimierung messen
GCs sind deutlicher und die Auswirkungen von Speicheroptimierungen lassen sich leichter messen. Wenn durch eine Optimierung der Speicherdruck reduziert wird, werden weniger GCs ausgeführt.
Um die Auswirkungen der Optimierung zu messen, messen Sie in der Zeitachse des Profilers die Zeit zwischen den GCs. Sie sehen dann, dass zwischen den GCs mehr Zeit vergeht.
Die Verbesserungen beim Arbeitsspeicher haben folgende Auswirkungen:
- Die Wahrscheinlichkeit von Abstürzen aufgrund von fehlendem Arbeitsspeicher wird wahrscheinlich reduziert, wenn die App nicht ständig unter Arbeitsspeicherdruck steht.
- Weniger GCs verbessern die Jitter-Messwerte, insbesondere den P99. Das liegt daran, dass GCs zu CPU-Konflikten führen, was dazu führen kann, dass Rendering-Aufgaben während der GC verschoben werden.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Analyse und Optimierung des App-Starts {:#app-startup-analysis-optimization}
- Eingefrorene Frames
- Makrobenchmark schreiben