W tym dokumencie znajdziesz sprawdzone metody, które pomogą Ci zdiagnozować problemy i zadbać o prawidłowe działanie profili referencyjnych, aby w pełni wykorzystać ich zalety.
Problemy z kompilacją
Jeśli skopiujesz przykładowy profil bazowy z aplikacji Nowości na Androidzie, podczas zadania dotyczącego profilu bazowego mogą wystąpić błędy mówiące, że testów nie można uruchomić na emulatorze:
./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):
...
Błędy te występują, ponieważ Now na Androidzie używa urządzenia zarządzanego przez Gradle do generowania profilu podstawowego. Błędy są oczekiwane, ponieważ zwykle nie należy uruchamiać testów porównawczych wydajności na emulatorze. Podczas generowania profili podstawowych nie zbierasz danych o wydajności, więc możesz dla wygody uruchomić zbieranie profili podstawowych na emulatorach. Aby używać Profilów podstawowych z emulatorem, wykonaj kompilację i instalację z poziomu wiersza poleceń, a następnie ustaw argument, aby włączyć reguły Profilów podstawowych:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Możesz też utworzyć niestandardową konfigurację uruchomienia w Android Studio, aby włączyć profile referencyjne na emulatorach. W tym celu kliknij Uruchom > Edytuj konfiguracje:

Problemy przy instalacji
Sprawdź, czy plik APK lub AAB, który sprawdzasz, pochodzi z wersji, która zawiera profile podstawowe:
- W Android Studio kliknij Build > Analyze APK (Utwórz > Przeanalizuj APK).
- Otwórz plik AAB lub APK.
- Jeśli sprawdzasz AAB, profil znajduje się na stronie
/BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof
. Jeśli sprawdzasz plik APK, profil znajduje się na stronie/assets/dexopt/baseline.prof
.

Profile bazowe muszą być kompilowane na urządzeniu, na którym działa aplikacja. Gdy instalujesz aplikację za pomocą Sklepu Play, Android Studio lub narzędzia wiersza poleceń Gradle Wrapper, kompilacja na urządzeniu odbywa się automatycznie. Gdy aplikacja jest instalowana za pomocą innych narzędzi, biblioteka Jetpacka ProfileInstaller
odpowiada za umieszczanie profili w kolejce do kompilacji podczas następnego procesu optymalizacji DEX w tle. W takich przypadkach, jeśli chcesz mieć pewność, że używane są Profile bazowe, konieczne może być wymuszenie ich kompilacji. ProfileVerifier
umożliwia sprawdzenie stanu instalacji i kompilacji profilu, jak w tym przykładzie:
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); } }
Te kody wyników wskazują przyczynę niektórych problemów:
RESULT_CODE_COMPILED_WITH_PROFILE
- Profil jest instalowany, kompilowany i używany za każdym razem, gdy uruchamiasz aplikację. To jest wynik, który chcesz zobaczyć.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
- W uruchamianym pliku APK nie znaleziono profilu. Jeśli widzisz ten błąd, upewnij się, że używasz wersji, która zawiera profile bazowe, oraz że plik APK zawiera profil.
RESULT_CODE_NO_PROFILE
- Podczas instalowania aplikacji w sklepie z aplikacjami lub menedżerze pakietów nie zainstalowano profilu tej aplikacji. Główną przyczyną tego kodu błędu jest to, że instalator profilu nie został uruchomiony z powodu wyłączenia
ProfileInstallerInitializer
. Pamiętaj, że gdy zgłoszony zostanie ten błąd, w pliku APK aplikacji nadal jest obecny profil. Jeśli nie można znaleźć profilu, zwracany jest kod błęduRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
- W pliku APK lub AAB znaleziono profil i został on umieszczony w kole do skompilowania. Gdy profil zostanie zainstalowany przez
ProfileInstaller
, zostanie umieszczony w kolejce do skompilowania następnym razem, gdy system wykona optymalizację DEX w tle. Profil nie jest aktywny, dopóki kompilacja nie zostanie ukończona. Nie próbuj porównywać profili bazowych, dopóki kompilacja nie zostanie ukończona. Może być konieczne wymuszenie kompilacji profili bazowych. Ten błąd nie wystąpi, jeśli aplikacja zostanie zainstalowana ze sklepu z aplikacjami lub menedżera pakietów na urządzeniu z Androidem 9 (interfejs API 28) lub nowszym, ponieważ kompilacja jest wykonywana podczas instalacji. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
- Zainstalowany jest niepasujący profil, a aplikacja została skompilowana z jego użyciem.
Jest to wynik instalacji za pomocą Sklepu Google Play lub menedżera pakietów.
Ten wynik różni się od
RESULT_CODE_COMPILED_WITH_PROFILE
, ponieważ profil niezgodny z modelem bazowym zawiera tylko metody, które są wspólne dla profilu i aplikacji. Profil jest więc mniejszy niż oczekiwano, a w jego przypadku zostanie skompilowanych mniej metod niż w przypadku profilu bazowego. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
nie może zapisać pliku pamięci podręcznej z wynikiem weryfikacji. Może się tak zdarzyć, jeśli wystąpił problem z uprawnieniami folderu aplikacji lub jeśli na urządzeniu jest za mało wolnego miejsca na dysku.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
obsługuje tylko Androida 9 (poziom interfejsu API 28) i nowszych wersji. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
- Podczas wysyłania zapytania do pakietu aplikacji
PackageManager
wystąpi błądPackageManager.NameNotFoundException
. To powinno się zdarzać rzadko. Spróbuj odinstalować aplikację i wszystko ponownie zainstalować. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- Istnieje plik pamięci podręcznej z poprzednimi wynikami weryfikacji, ale nie można go odczytać. Nie powinno się to zdarzać zbyt często. Spróbuj odinstalować aplikację i wszystko ponownie zainstalować.
Korzystanie z narzędzia ProfileVerifier w wersji produkcyjnej
W środowisku produkcyjnym możesz używać ProfileVerifier
w połączeniu z bibliotekami raportowania analitycznego, takimi jak Google Analytics dla Firebase, aby generować zdarzenia analityczne wskazujące stan profilu. Dzięki temu możesz na przykład szybko otrzymywać powiadomienia, gdy zostanie opublikowana nowa wersja aplikacji, która nie zawiera profili bazowych.
Wymuszanie kompilacji profili podstawowych
Jeśli stan kompilacji profili podstawowych to RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
, możesz wymusić natychmiastową kompilację, używając adb
:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Sprawdzanie stanu kompilacji bez narzędzia ProfileVerifier
Jeśli nie używasz ProfileVerifier
, możesz sprawdzić stan kompilacji za pomocą adb
, ale nie uzyskasz tak szczegółowych informacji jak w przypadku ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
Użycie adb
spowoduje wyświetlenie czegoś podobnego do tego:
[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]
Wartość stanu wskazuje stan kompilacji profilu i może przyjmować jedną z tych wartości:
Stan kompilacji | Znaczenie |
---|---|
speed‑profile |
Kompilowany profil istnieje i jest używany. |
verify |
Nie ma skompilowanego profilu. |
Stan verify
nie oznacza, że plik APK lub AAB nie zawiera profilu, ponieważ może on zostać umieszczony w kole do skompilowania przez następne zadanie optymalizacji DEX w tle.
Wartość powodu określa, co powoduje skompilowanie profilu. Może to być jedna z tych wartości:
Przyczyna | Znaczenie |
---|---|
install‑dm
|
Profil bazowy został skompilowany ręcznie lub przez Google Play po zainstalowaniu aplikacji. |
bg‑dexopt
|
Profil został skompilowany, gdy urządzenie było nieaktywne. Może to być profil podstawowy lub profil zebrany podczas korzystania z aplikacji. |
cmdline
|
Kompilacja została uruchomiona za pomocą adb. Może to być profil podstawowy lub profil zebrany podczas korzystania z aplikacji. |
problemy z wydajnością,
W tej sekcji znajdziesz sprawdzone metody prawidłowego definiowania i porównywania profili podstawowych, aby w pełni z nich korzystać.
Prawidłowo porównywać dane o rozruchu
Profile podstawowe będą skuteczniejsze, jeśli dane dotyczące startupów są dobrze zdefiniowane. Te 2 kluczowe dane to czas do początkowego wyświetlenia (TTID) i czas do pełnego wyświetlenia (TTFD).
TTID to moment, w którym aplikacja wyświetla pierwszą klatkę. Ważne, aby ten komunikat był jak najkrótszy, ponieważ wyświetlanie czegokolwiek informuje użytkownika, że aplikacja działa. Możesz nawet wyświetlić nieokreślony wskaźnik postępu, aby pokazać, że aplikacja reaguje na działania użytkownika.
TTFD to czas, w którym można wchodzić w interakcję z aplikacją. Pamiętaj, aby trzymać się jak najkrótszych formuł, aby nie irytować użytkowników. Jeśli prawidłowo sygnalizujesz TTFD, informujesz system, że kod, który jest uruchamiany na drodze do TTFD, jest częścią uruchamiania aplikacji. W efekcie system z większym prawdopodobieństwem umieści ten kod w profilu.
Aby zwiększyć responsywność aplikacji, utrzymuj wartości TTID i TTFD na jak najniższym poziomie.
System może wykrywać identyfikatory TTID, wyświetlać je w Logcat i zgłaszać jako część danych porównawczych dotyczących uruchamiania. System nie jest jednak w stanie określić TTFD, dlatego to aplikacja musi zgłaszać, kiedy osiągnie stan interaktywny. Możesz to zrobić, dzwoniąc pod numer reportFullyDrawn()
lub ReportDrawn
, jeśli używasz Jetpack Compose. Jeśli masz kilka zadań w tle, które muszą zostać ukończone, zanim aplikacja zostanie uznana za całkowicie wycofaną, możesz użyć FullyDrawnReporter
, jak opisano w artykule Poprawianie dokładności czasu uruchamiania.
Profile bibliotek i profile niestandardowe
Podczas porównywania wpływu profili może być trudno oddzielić korzyści wynikające z profili aplikacji od profili udostępnionych przez biblioteki, takie jak biblioteki Jetpacka. Gdy kompilujesz plik APK, wtyczka Androida do obsługi Gradle dodaje wszystkie profile w bibliotekach zależnych, a także Twój profil niestandardowy. Jest to dobre rozwiązanie do optymalizacji ogólnej wydajności i zalecane w przypadku wersji do wydania. Utrudnia to jednak pomiar dodatkowego wzrostu skuteczności wynikającego z profilu niestandardowego.
Szybki sposób na ręczne sprawdzenie dodatkowej optymalizacji zapewnianej przez profil niestandardowy to jego usunięcie i wykonywanie testów porównawczych. Następnie zastąp go i ponownie uruchom testy porównawcze. Porównanie tych dwóch zestawień pozwoli Ci zobaczyć, jaką optymalizację zapewniają same profile biblioteki i jakie zapewniają profile biblioteki oraz Twój profil niestandardowy.
Automatyczny sposób porównywania profili polega na utworzeniu nowej wersji, która zawiera tylko profile biblioteki, a nie profil niestandardowy. Porównaj wyniki testów porównawczych z tego wariantu z wariantem opublikowanym, który zawiera zarówno profile biblioteki, jak i Twoje profile niestandardowe. Ten przykład pokazuje, jak skonfigurować wariant, który obejmuje tylko profile bibliotek. Dodaj nową odmianę o nazwie releaseWithoutCustomProfile
do modułu konsumenta profilu, który jest zwykle modułem aplikacji:
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")) } } }
Poprzedni przykład kodu usuwa zależność baselineProfile
ze wszystkich wariantów i selektywnie stosuje ją tylko do wariantu release
. Może się wydawać nielogiczne, że profile bibliotek są nadal dodawane, gdy usuniesz zależność od modułu producenta profili. Ten moduł odpowiada jednak tylko za wygenerowanie profilu niestandardowego. Wtyczka Android Gradle jest nadal używana we wszystkich wariantach i odpowiada za uwzględnianie profili bibliotek.
Musisz też dodać nowy wariant do modułu generatora profili. W tym przykładzie moduł producenta ma nazwę :baselineprofile
.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Podczas uruchamiania testu porównawczego w Android Studio wybierz wariant releaseWithoutCustomProfile
, aby mierzyć wydajność tylko z profilami biblioteki, lub wariant release
, aby mierzyć wydajność z profilami biblioteki i profilami niestandardowymi.
Unikaj uruchamiania aplikacji z dostępem do I/O
Jeśli podczas uruchamiania aplikacja wykonuje wiele wywołań we/wy lub wywołań sieciowych, może to negatywnie wpłynąć zarówno na czas uruchamiania aplikacji, jak i na dokładność testów porównawczych czasu uruchamiania. Te obciążające wywołania mogą trwać nieokreślony czas, który może się zmieniać w czasie, a nawet między iteracjami tego samego testu porównawczego. Wywołania I/O są zazwyczaj lepsze niż wywołania sieciowe, ponieważ na te ostatnie mogą wpływać czynniki zewnętrzne i właściwości samego urządzenia. Unikaj wywołań sieci podczas uruchamiania. Jeśli użycie jednej z tych opcji jest nieuniknione, użyj I/O.
Zalecamy, aby architektura aplikacji obsługiwała uruchamianie aplikacji bez wywołań sieciowych ani wejść/wyjść, nawet jeśli ma to służyć tylko do porównywania czasu uruchamiania. Dzięki temu uzyskasz możliwie najmniejszą zmienność między różnymi iteracjami wartości referencyjnych.
Jeśli Twoja aplikacja korzysta z Hilt, możesz podać fałszywe implementacje ograniczone przez I/O podczas testowania porównawczego w Microbenchmark i Hilt.
Obejmowanie wszystkich ważnych ścieżek użytkownika
Podczas generowania profilu podstawowego musisz dokładnie uwzględnić wszystkie ważne ścieżki użytkowników. Profile podstawowe nie poprawiają ścieżek użytkowników, które nie są uwzględnione. Najskuteczniejsze profile bazowe obejmują wszystkie typowe ścieżki użytkownika podczas uruchamiania aplikacji oraz ścieżki użytkownika w aplikacji, które mają wpływ na wydajność, np. przewijanie list.