In diesem Dokument finden Sie Best Practices, mit denen Sie Probleme diagnostizieren und dafür sorgen können, dass Ihre Baseline-Profile richtig funktionieren und Ihnen den größtmöglichen Nutzen bieten.
Probleme beim Erstellen
Wenn Sie das Beispiel für Baseline-Profile in der Beispiel-App Jetzt bei Android kopiert haben, treten bei der Aufgabe „Baseline-Profil“ möglicherweise Testfehler auf, die darauf hinweisen, dass die Tests nicht auf einem Emulator ausgeführt werden können:
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
Die Fehler treten auf, weil Now in Android für die Generierung des Baseline-Profils ein von Gradle verwaltetes Gerät verwendet. Die Fehler sind zu erwarten, da Sie Leistungstests in der Regel nicht auf einem Emulator ausführen sollten. Da Sie beim Erstellen von Baseline-Profilen jedoch keine Leistungsmesswerte erheben, können Sie die Erhebung von Baseline-Profilen zur Vereinfachung auf Emulatoren ausführen. Wenn Sie Baseline Profiles mit einem Emulator verwenden möchten, führen Sie den Build und die Installation über die Befehlszeile aus und legen Sie ein Argument fest, um Baseline Profiles-Regeln zu aktivieren:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Alternativ können Sie in Android Studio eine benutzerdefinierte Ausführungskonfiguration erstellen, um Baseline-Profile auf Emulatoren zu aktivieren. Wählen Sie dazu Ausführen > Konfigurationen bearbeiten aus:

Installationsprobleme
Prüfen Sie, ob das APK oder AAB, das Sie prüfen, aus einer Buildvariante stammt, die Baseline-Profile enthält:
- Wählen Sie in Android Studio Build > APK analysieren aus.
- Öffnen Sie Ihr AAB oder APK.
- Wenn Sie eine AAB prüfen, finden Sie das Profil unter
/BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof
. Wenn Sie ein APK prüfen, befindet sich das Profil unter/assets/dexopt/baseline.prof
.

Baseline-Profile müssen auf dem Gerät kompiliert werden, auf dem die App ausgeführt wird. Wenn Sie die App über den Play Store, Android Studio oder das Gradle Wrapper-Befehlszeilentool installieren, erfolgt die On-Device-Kompilierung automatisch. Wenn die App mit anderen Tools installiert wird, ist die Jetpack-Bibliothek ProfileInstaller
dafür verantwortlich, die Profile während des nächsten DEX-Optimierungsprozesses im Hintergrund zur Kompilierung einzureihen. Wenn Sie in diesen Fällen dafür sorgen möchten, dass Ihre Baseline-Profile verwendet werden, müssen Sie möglicherweise die Kompilierung von Baseline-Profilen erzwingen. Mit ProfileVerifier
können Sie den Status der Profilinstallation und ‑kompilierung abfragen, wie im folgenden Beispiel gezeigt:
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
Die folgenden Ergebniscodes geben Hinweise auf die Ursache einiger Probleme:
RESULT_CODE_COMPILED_WITH_PROFILE
- Das Profil wird installiert, kompiliert und bei jedem Ausführen der App verwendet. Das ist das gewünschte Ergebnis.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
- Im ausgeführten APK wird kein Profil gefunden. Wenn dieser Fehler auftritt, prüfen Sie, ob Sie eine Buildvariante mit Baseline-Profilen verwenden und ob die APK ein Profil enthält.
RESULT_CODE_NO_PROFILE
- Bei der Installation der App über den App-Shop oder den Paketmanager wurde kein Profil für diese App installiert. Der Hauptgrund für diesen Fehlercode ist, dass der Profil-Installer nicht ausgeführt wurde, weil
ProfileInstallerInitializer
deaktiviert war. Hinweis: Wenn dieser Fehler gemeldet wird, wurde im APK der Anwendung trotzdem ein eingebettetes Profil gefunden. Wenn ein eingebettetes Profil nicht gefunden wird, wird der FehlercodeRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
zurückgegeben. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
- Im APK oder AAB wird ein Profil gefunden und zur Kompilierung in die Warteschlange gestellt. Wenn ein Profil von
ProfileInstaller
installiert wird, wird es zur Kompilierung in die Warteschlange gestellt, wenn die DEX-Optimierung im Hintergrund das nächste Mal vom System ausgeführt wird. Das Profil ist erst aktiv, wenn die Kompilierung abgeschlossen ist. Versuchen Sie erst, Benchmarks für Ihre Baseline-Profile zu erstellen, wenn die Kompilierung abgeschlossen ist. Möglicherweise müssen Sie die Erstellung von Baseline-Profilen erzwingen. Dieser Fehler tritt nicht auf, wenn die App über den App-Shop oder den Paketmanager auf Geräten mit Android 9 (API 28) oder höher installiert wird, da die Kompilierung während der Installation erfolgt. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
- Ein nicht übereinstimmendes Profil ist installiert und die App wurde damit kompiliert.
Dies ist das Ergebnis der Installation über den Google Play Store oder den Paketmanager.
Dieses Ergebnis unterscheidet sich von
RESULT_CODE_COMPILED_WITH_PROFILE
, da im nicht übereinstimmenden Profil nur Methoden kompiliert werden, die zwischen dem Profil und der App noch gemeinsam genutzt werden. Das Profil ist also kleiner als erwartet und es werden weniger Methoden kompiliert als im Baseline-Profil enthalten waren. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
kann die Cachedatei mit den Bestätigungsergebnissen nicht schreiben. Das kann entweder daran liegen, dass etwas mit den Berechtigungen für den App-Ordner nicht stimmt, oder dass auf dem Gerät nicht genügend freier Speicherplatz vorhanden ist.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
unterstützt nur Android 9 (API-Level 28) und höher. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
- Ein
PackageManager.NameNotFoundException
wird ausgegeben, wenn derPackageManager
nach dem App-Paket abgefragt wird. Das sollte selten passieren. Deinstallieren Sie die App und installieren Sie alles neu. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- Es gibt eine Cachedatei mit vorherigen Bestätigungsergebnissen, die aber nicht gelesen werden kann. Das sollte selten passieren. Deinstallieren Sie die App und installieren Sie alles neu.
ProfileVerifier in der Produktion verwenden
In der Produktion können Sie ProfileVerifier
in Verbindung mit Bibliotheken für Analyseberichte wie Google Analytics for Firebase verwenden, um Analyseereignisse zu generieren, die den Profilstatus angeben. So werden Sie beispielsweise schnell benachrichtigt, wenn eine neue App-Version veröffentlicht wird, die keine Baseline-Profile enthält.
Zusammenstellung von Baseline-Profilen erzwingen
Wenn der Kompilierungsstatus Ihrer Baseline-Profile RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
lautet, können Sie die sofortige Kompilierung mit adb
erzwingen:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Kompilierungsstatus ohne ProfileVerifier prüfen
Wenn Sie ProfileVerifier
nicht verwenden, können Sie den Kompilierungsstatus mit adb
prüfen. Dieser Befehl bietet jedoch nicht so detaillierte Informationen wie ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
Die Ausgabe von adb
sieht in etwa so aus:
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
Der Statuswert gibt den Status der Profilkompilierung an und kann einen der folgenden Werte haben:
Status der Zusammenstellung | Bedeutung |
---|---|
speed‑profile |
Es gibt ein kompiliertes Profil, das verwendet wird. |
verify |
Es gibt kein kompiliertes Profil. |
Ein Status von verify
bedeutet nicht, dass die APK oder AAB kein Profil enthält, da sie von der nächsten DEX-Optimierungsaufgabe im Hintergrund zur Kompilierung in die Warteschlange gestellt werden kann.
Der Grundwert gibt an, was die Kompilierung des Profils auslöst. Er kann einen der folgenden Werte haben:
Grund | Bedeutung |
---|---|
install‑dm
|
Ein Baseline-Profil wurde manuell oder von Google Play erstellt, als die App installiert wurde. |
bg‑dexopt
|
Ein Profil wurde erstellt, während Ihr Gerät inaktiv war. Dies kann ein Baseline-Profil oder ein Profil sein, das während der App-Nutzung erfasst wurde. |
cmdline
|
Die Kompilierung wurde mit adb ausgelöst. Dies kann ein Baseline-Profil oder ein Profil sein, das während der App-Nutzung erfasst wurde. |
Leistungsprobleme
In diesem Abschnitt finden Sie einige Best Practices für die korrekte Definition und Benchmarking Ihrer Baseline-Profile, damit Sie sie optimal nutzen können.
Startmesswerte richtig benchmarken
Ihre Baseline-Profile sind effektiver, wenn Ihre Startmesswerte klar definiert sind. Die beiden wichtigsten Messwerte sind Zeit bis zur ersten Anzeige (TTID) und Zeit bis zur vollständigen Anzeige (TTFD).
TTID ist der Zeitpunkt, zu dem die App ihren ersten Frame zeichnet. Es ist wichtig, diese Zeit so kurz wie möglich zu halten, da der Nutzer sieht, dass die App ausgeführt wird. Sie können sogar eine unbestimmte Fortschrittsanzeige anzeigen, um zu zeigen, dass die App reaktionsschnell ist.
TTFD ist der Zeitpunkt, zu dem die App tatsächlich verwendet werden kann. Es ist wichtig, diesen Vorgang so kurz wie möglich zu halten, um Frustration bei den Nutzern zu vermeiden. Wenn Sie TTFD korrekt signalisieren, teilen Sie dem System mit, dass der Code, der auf dem Weg zum TTFD ausgeführt wird, Teil des App-Starts ist. Das System fügt diesen Code dann mit höherer Wahrscheinlichkeit in das Profil ein.
Halten Sie sowohl die TTID als auch die TTFD so niedrig wie möglich, damit Ihre App reaktionsschnell ist.
Das System kann die TTID erkennen, in Logcat anzeigen und als Teil der Start-Benchmarks melden. Das System kann die TTFD jedoch nicht ermitteln. Es liegt in der Verantwortung der App, zu melden, wenn ein vollständig gezeichneter interaktiver Status erreicht wird. Rufen Sie dazu reportFullyDrawn()
oder ReportDrawn
auf, wenn Sie Jetpack Compose verwenden. Wenn mehrere Hintergrundaufgaben abgeschlossen werden müssen, bevor die App vollständig dargestellt wird, können Sie FullyDrawnReporter
verwenden, wie unter Genauigkeit des Startzeitpunkts verbessern beschrieben.
Bibliotheksprofile und benutzerdefinierte Profile
Beim Benchmarking der Auswirkungen von Profilen kann es schwierig sein, die Vorteile der Profile Ihrer App von denen von Bibliotheken wie Jetpack-Bibliotheken zu unterscheiden. Wenn Sie Ihr APK erstellen, fügt das Android-Gradle-Plug-in alle Profile in Bibliotheksabhängigkeiten sowie Ihr benutzerdefiniertes Profil hinzu. Dies ist gut für die Optimierung der Gesamtleistung und wird für Release-Builds empfohlen. Es ist jedoch schwierig zu messen, wie viel zusätzliche Leistung durch Ihr benutzerdefiniertes Profil erzielt wird.
Wenn Sie die zusätzlichen Optimierungen Ihres benutzerdefinierten Profils manuell sehen möchten, können Sie es entfernen und Ihre Benchmarks ausführen. Ersetzen Sie es dann und führen Sie die Benchmarks noch einmal aus. Wenn Sie die beiden vergleichen, sehen Sie die Optimierungen, die allein durch die Bibliotheksprofile und durch die Bibliotheksprofile sowie Ihr benutzerdefiniertes Profil erzielt werden.
Eine automatisierte Möglichkeit zum Vergleichen von Profilen besteht darin, eine neue Buildvariante zu erstellen, die nur die Bibliotheksprofile und nicht Ihr benutzerdefiniertes Profil enthält. Vergleichen Sie die Benchmarks dieser Variante mit der Release-Variante, die sowohl die Bibliotheksprofile als auch Ihre benutzerdefinierten Profile enthält. Im folgenden Beispiel wird gezeigt, wie Sie die Variante einrichten, die nur Bibliotheksprofile enthält. Fügen Sie Ihrem Profilmodul für Nutzer (in der Regel das App-Modul) eine neue Variante mit dem Namen releaseWithoutCustomProfile
hinzu:
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
Im vorherigen Codebeispiel wird die baselineProfile
-Abhängigkeit aus allen Varianten entfernt und selektiv nur auf die release
-Variante angewendet. Es mag kontraintuitiv erscheinen, dass die Bibliotheksprofile weiterhin hinzugefügt werden, wenn die Abhängigkeit vom Modul „Profile Producer“ entfernt wird. Dieses Modul ist jedoch nur für die Generierung Ihres benutzerdefinierten Profils verantwortlich. Das Android Gradle-Plug-in wird weiterhin für alle Varianten ausgeführt und ist für das Einbeziehen von Bibliotheksprofilen verantwortlich.
Außerdem müssen Sie die neue Variante dem Modul „Profilgenerator“ hinzufügen. In diesem Beispiel heißt das Producer-Modul :baselineprofile
.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Wenn Sie den Benchmark aus Android Studio ausführen, wählen Sie eine releaseWithoutCustomProfile
-Variante aus, um die Leistung nur mit Bibliotheksprofilen zu messen, oder eine release
-Variante, um die Leistung mit Bibliotheks- und benutzerdefinierten Profilen zu messen.
I/O-gebundenen App-Start vermeiden
Wenn Ihre App beim Starten viele E/A- oder Netzwerkaufrufe ausführt, kann sich das sowohl auf die Startzeit der App als auch auf die Genauigkeit des Benchmarkings auswirken. Diese aufwendigen Aufrufe können unbestimmte Zeit in Anspruch nehmen, die sich im Laufe der Zeit und sogar zwischen den Iterationen desselben Benchmarks ändern kann. I/O-Aufrufe sind im Allgemeinen besser als Netzwerkaufrufe, da letztere von Faktoren außerhalb des Geräts und auf dem Gerät selbst beeinflusst werden können. Vermeiden Sie Netzwerkaufrufe während des Starts. Wenn die Verwendung der einen oder anderen Methode unvermeidlich ist, verwenden Sie die I/O-Methode.
Wir empfehlen, dass Ihre App-Architektur das Starten der App ohne Netzwerk- oder I/O-Aufrufe unterstützt, auch wenn Sie sie nur für das Benchmarking des Starts verwenden. So lässt sich die Variabilität zwischen den verschiedenen Iterationen Ihrer Benchmarks so gering wie möglich halten.
Wenn Ihre App Hilt verwendet, können Sie beim Benchmarking in Microbenchmark und Hilt gefälschte E/A-gebundene Implementierungen angeben.
Alle wichtigen User Journeys abdecken
Es ist wichtig, alle wichtigen User Journeys bei der Erstellung des Baseline-Profils genau abzudecken. Nicht abgedeckte User Journeys werden durch Baseline-Profile nicht verbessert. Die effektivsten Baseline-Profile umfassen alle gängigen Start-up-User Journeys sowie leistungssensible In-App-User Journeys wie scrollbare Listen.