Der App-Start stellt den ersten Eindruck Ihrer App bei Nutzern dar. Da die Nutzer nicht warten möchten, solltest du darauf achten, dass deine App schnell startet. Um Ihnen zu zeigen, wie ein echtes App-Entwicklungsteam Probleme beim Start seiner App gefunden und diagnostiziert hat, hat das Gmail Wear OS-Team wie folgt vorgegangen.
Das Gmail Wear OS-Team führte eine Optimierung durch, mit besonderem Augenmerk auf die App-Start- und Laufzeit-Rendering-Leistung, um die Kriterien des Teams für die App-Leistung zu erfüllen. Aber selbst wenn Sie keine spezifischen Grenzwerte für das Targeting haben, können Sie den App-Start fast immer verbessern, wenn Sie sich die Zeit nehmen, dies zu untersuchen.
Trace erfassen und App-Start ansehen
Erfassen Sie einen Trace, der den App-Start zur genaueren Prüfung in Perfetto oder Android Studio enthält, um mit der Analyse zu beginnen. In dieser Fallstudie wird Perfetto verwendet, weil es zeigt, was im Gerätesystem außerhalb Ihrer Anwendung passiert. Wenn Sie den Trace in Perfetto hochladen, sieht es so aus:
Da der Schwerpunkt auf der Verbesserung des App-Starts liegt, suchen Sie die Zeile mit dem benutzerdefinierten Messwert Android-App-Starts. Es ist hilfreich, ihn oben in der Ansicht anzupinnen, indem Sie auf das Stecknadelsymbol klicken, das erscheint, wenn Sie den Mauszeiger auf die Zeile bewegen. Der Balken oder Slice, den Sie in der Zeile Android-App-Starts sehen, gibt den Zeitraum an, den der App-Start abdeckt, bis der erste App-Frame auf dem Bildschirm zu sehen ist. Suchen Sie daher dort nach Problemen oder Engpässen.
Der Messwert Android-App-Starts entspricht der Zeit bis zur ersten Anzeige, auch wenn Sie reportFullyDrawn()
verwenden. Wenn Sie die Zeit bis zur vollständigen Anzeige ermitteln möchten, suchen Sie im Perfetto-Suchfeld nach reportFullyDrawn()
.
Hauptthread prüfen
Untersuchen Sie zuerst, was im Hauptthread passiert. Der Hauptthread ist sehr wichtig, da dort normalerweise das gesamte UI-Rendering stattfindet. Wenn er blockiert ist, kann nichts gezeichnet werden und Ihre Anwendung scheint eingefroren zu sein. Daher sollten Sie dafür sorgen, dass im Hauptthread keine lang andauernden Vorgänge ausgeführt werden.
Suchen Sie die Zeile mit dem Paketnamen Ihrer App und maximieren Sie ihn, um den Hauptthread zu finden. Die beiden Zeilen mit demselben Namen wie das Paket (normalerweise die ersten beiden Zeilen im Abschnitt) stellen den Hauptthread dar. Von den beiden Hauptthreadzeilen repräsentiert die erste den CPU-Status und die zweite Zeile Tracepoints. Pinne die beiden Hauptthreadzeilen unter dem Messwert Android-App-Starts an.
Im „runnable“-Zustand verbrachte Zeit und CPU-Konflikt
Für eine aggregierte Ansicht der CPU-Aktivität während des Anwendungsstarts ziehen Sie den Cursor über den Hauptthread, um die Startzeit der Anwendung zu erfassen. Der Bereich Thread-Status wird eingeblendet. Hier sehen Sie die Gesamtzeit, die innerhalb des ausgewählten Zeitraums in jedem CPU-Status verbracht wurde.
Sieh dir die Zeit an, die im Status Runnable
verbracht wurde. Wenn ein Thread den Status Runnable
hat, kann er zwar ausgeführt werden, es werden aber keine Aufgaben geplant. Dies könnte darauf hinweisen, dass das Gerät stark ausgelastet ist und keine Aufgaben mit hoher Priorität planen kann. Die oberste, für Nutzer sichtbare Anwendung hat bei der Planung die höchste Priorität. Daher weist ein inaktiver Hauptthread häufig darauf hin, dass intensive Prozesse innerhalb der Anwendung, z. B. das Rendering von Animationen, mit dem Hauptthread um die CPU-Zeit konkurrieren.
Je höher das Verhältnis der Zeit im Status Runnable
zur Zeit im Status Running
ist, desto wahrscheinlicher treten CPU-Konflikte auf. Wenn Sie Leistungsprobleme auf diese Weise untersuchen, sollten Sie sich zuerst auf den Frame mit der längsten Ausführung konzentrieren und auf kleinere konzentrieren.
Bei der Analyse der Zeit mit dem Status „Runnable
“ solltest du die Gerätehardware berücksichtigen.
Da die dargestellte App auf einem Wearable-Gerät mit zwei CPUs ausgeführt wird, wird im Status Runnable
mehr Zeit benötigt und es besteht mehr CPU-Konflikte mit anderen Prozessen als bei einem Gerät mit mehr CPUs. Obwohl für eine typische Telefon-App mehr Zeit im Status Runnable
verbracht wird als erwartet, kann er im Kontext von Wearables verständlich sein.
Dauer: OpenDexFilesFromOat*
Prüfen Sie jetzt die in OpenDexFilesFromOat*
verbrachte Zeit. Im Trace geschieht dies zur selben Zeit wie das bindApplication
-Slice. Dieses Segment gibt die Zeit an, die zum Lesen der DEX-Dateien der Anwendung benötigt wird.
Blockierte Binder-Transaktionen
Prüfen Sie als Nächstes die Binder-Transaktionen. Binder-Transaktionen stellen Aufrufe zwischen dem Client und dem Server dar: In diesem Fall ruft die App (der Client) das Android-System (Server) mit einer binder transaction
auf und der Server antwortet mit einer binder
reply
. Achten Sie darauf, dass die Anwendung während des Starts keine unnötigen Bindertransaktionen ausführt, da diese das Risiko von CPU-Konflikten erhöhen. Wenn möglich, verschieben Sie Arbeiten, die Binderaufrufe beinhalten, nach der Startphase der Anwendung. Wenn Sie Binder-Transaktionen ausführen müssen, achten Sie darauf, dass diese nicht länger als die VSync-Aktualisierungsrate Ihres Geräts dauern.
Die erste Binder-Transaktion, die normalerweise zur selben Zeit wie das ActivityThreadMain
-Slice erfolgt, scheint in diesem Fall recht lang zu sein. Führen Sie die folgenden Schritte aus, um mehr darüber zu erfahren, was möglicherweise passiert:
- Klicken Sie auf das gewünschte Segment der Binder-Transaktion, um die zugehörige Binder-Antwort zu sehen und mehr über die Priorisierung der Binder-Transaktion zu erfahren.
Um die binäre Antwort zu sehen, gehen Sie zum Feld Aktuelle Auswahl und klicken Sie im Abschnitt Folgende Threads auf Binder-Antwort. Im Feld Thread erfahren Sie auch, in welchem Thread die Binder-Antwort erfolgt, wenn Sie manuell dorthin navigieren möchten. Es befindet sich in einem anderen Prozess. Es wird eine Zeile angezeigt, die die Binder-Transaktion und die Antwort verbindet.
Wenn Sie sehen möchten, wie der Systemserver diese Bindertransaktion verarbeitet, pinnen Sie die Threads Cpu 0 und Cpu 1 oben in Ihrem Bildschirm an.
Ermitteln Sie die Systemprozesse, die die Binder-Antwort verarbeiten, indem Sie die Segmente finden, die den Namen des Binder-Antwort-Threads enthalten, in diesem Fall "Binder:687_11 [2542]". Klicken Sie auf die entsprechenden Systemprozesse, um weitere Informationen zur Bindertransaktion zu erhalten.
Sehen Sie sich diesen Systemprozess an, der mit der Binder-Transaktion von Interesse verknüpft ist, die auf CPU 0 stattfindet:
Im Endstatus wird Runnable (Preempted)
angezeigt. Das bedeutet, dass sich der Prozess verzögert, weil die CPU etwas anderes tut. Maximieren Sie die Zeilen Ftrace-Ereignisse, um zu sehen, was vorzeitig beendet wird. Scrollen Sie auf dem Tab Ftrace-Ereignisse, der verfügbar wird, durch und suchen Sie nach Ereignissen im Zusammenhang mit dem relevanten Binder-Thread „Binder:687_11 [2542]“. Ungefähr zu der Zeit, zu der der Systemprozess vorzeitig beendet wird, sind zwei Systemserverereignisse aufgetreten, die das Argument „decon“ enthalten und sich somit auf den Display-Controller beziehen. Das klingt vernünftig, weil der Display-Controller die Frames
auf dem Bildschirm platziert – eine wichtige Aufgabe. Dann lassen Sie die Ereignisse so, wie sie sind.
JIT-Aktivität
Wenn Sie die JIT-Kompilierung (Just-in-Time Compilation) untersuchen möchten, erweitern Sie die zu Ihrer Anwendung gehörenden Prozesse, suchen Sie die beiden Zeilen mit dem Jit-Thread-Pool und pinnen Sie sie oben an Ihrer Ansicht an. Da diese Anwendung beim Start der Anwendung von Baseline Profiles profitiert, erfolgt bis zum Zeichnen des ersten Frames sehr wenig JIT-Aktivität. Dies wird durch das Ende des ersten Choreographer.doFrame
-Aufrufs dargestellt. Beachten Sie jedoch den Grund für einen langsamen Start JIT compiling void
, der darauf hindeutet, dass die Systemaktivität während des Tracepoint mit dem Label Application creation
viele Hintergrund-JIT-Aktivitäten verursacht. Fügen Sie zum Beheben dieses Problems die Ereignisse, die kurz nach dem Erstellen des ersten Frames auftreten, dem Baseline-Profil hinzu. Erweitern Sie dazu die Profilsammlung so, dass die Anwendung verwendet werden kann. In vielen Fällen können Sie dazu eine Zeile am Ende der Makro-Benchmark-Sammlung für das Referenzprofil einfügen. Diese wartet darauf, dass ein bestimmtes UI-Widget auf dem Bildschirm erscheint und anzeigt, dass der Bildschirm vollständig ausgefüllt ist.
Ergebnis
Als Ergebnis dieser Analyse hat das Wear OS-Team von Gmail folgende Verbesserungen vorgenommen:
- Da bei der Analyse der CPU-Aktivität beim Start der Anwendung Konflikte erkannt wurden, wurde das rotierende Ladesymbol ersetzt, mit dem angezeigt wurde, dass die Anwendung mit einem einzelnen statischen Bild geladen wird. Außerdem wurde der Ladebildschirm so verlängert, dass der Schimmerstatus, der zweite Bildschirmstatus, der angibt, dass die App geladen wird, zurückgestellt wurde, um CPU-Ressourcen freizugeben. Dadurch wurde die Latenz beim Starten der App um 50 % verbessert.
- Anhand der Zeit, die mit
OpenDexFilesFromOat*
und JIT-Aktivitäten verbracht wurde, konnten in R8 Baseline-Profile umgeschrieben werden. Dadurch wurde die Latenz beim Starten der Anwendung um 20 % verbessert.
Hier einige Tipps des Teams zur effizienten Analyse der App-Leistung:
- Richten Sie einen laufenden Prozess ein, der Traces und Ergebnisse automatisch erfasst. Richten Sie gegebenenfalls automatisiertes Tracing für Ihre Anwendung mithilfe von Benchmarking ein.
- Führen Sie A/B-Tests für Änderungen durch, die Ihrer Meinung nach die Leistung verbessern, und lehnen Sie sie ab, wenn sie dies nicht tun. Sie können die Leistung in verschiedenen Szenarien mithilfe der MacroBenchmark-Bibliothek messen.
Weitere Informationen finden Sie in den folgenden Ressourcen:
- Leistung: Stichprobenprofilerstellung mit Systrace verwenden – MAD-Skills
- Leistung: Profiler-Traces erfassen – MAD-Kenntnisse