1. Zanim zaczniesz
W tym module z kodem dowiesz się, jak generować profile podstawowe, aby optymalizować wydajność aplikacji, oraz jak weryfikować korzyści z ich stosowania.
Czego potrzebujesz
- Android Studio Hedgehog (2023.1.1) lub nowszy
- Wtyczka Androida do obsługi Gradle w wersji 8.0 lub nowszej
- Podstawowa wiedza o bibliotece Jetpack Macrobenchmark
- fizyczne urządzenie z Androidem 7 (poziom interfejsu API 24) lub nowszym,
Jakie zadania wykonasz
- Skonfiguruj projekt, aby używać generatorów profili podstawowych.
- Generuj profile podstawowe, aby zoptymalizować uruchamianie aplikacji i przewijanie.
- Sprawdź wzrost wydajności za pomocą biblioteki Jetpack Macrobenchmark.
Czego się nauczysz
- Profile podstawowe i sposób, w jaki mogą one poprawić wydajność aplikacji.
- Jak generować profile podstawowe.
- Wzrost wydajności dzięki profilom podstawowym.
2. Przygotowania
Aby rozpocząć, sklonuj repozytorium GitHub z wiersza poleceń za pomocą tego polecenia:
$ git clone https://github.com/android/codelab-android-performance.git
Możesz też pobrać 2 pliki ZIP:
Otwieranie projektu w Android Studio
- W oknie Welcome to Android Studio (Witamy w Android Studio) wybierz
Open an Existing Project (Otwórz istniejący projekt). - Wybierz folder
[Download Location]/codelab-android-performance/baseline-profiles. Upewnij się, że wybierasz katalogbaseline-profiles. - Gdy Android Studio zaimportuje projekt, sprawdź, czy możesz uruchomić moduł
app, aby skompilować przykładową aplikację, z którą będziesz później pracować.
Przykładowa aplikacja
W tym ćwiczeniu w Codelabs będziesz pracować z przykładową aplikacją JetSnack. Jest to wirtualna aplikacja do zamawiania przekąsek, która korzysta z Jetpack Compose.
Aby zmierzyć wydajność aplikacji, musisz poznać strukturę interfejsu i sposób działania aplikacji, aby mieć dostęp do elementów interfejsu z poziomu testów porównawczych. Uruchom aplikację i zapoznaj się z podstawowymi ekranami, zamawiając przekąski. Nie musisz znać szczegółów architektury aplikacji.

3. Czym są profile podstawowe
Profile bazowe zwiększają szybkość wykonywania kodu o około 30% od pierwszego uruchomienia, ponieważ unikają interpretacji i kroków kompilacji just-in-time (JIT) w przypadku uwzględnionych ścieżek kodu. Dzięki profilowi podstawowemu w aplikacji lub bibliotece środowisko wykonawcze Androida (ART) może optymalizować ścieżki kodu za pomocą kompilacji AOT (Ahead of Time), co zwiększa wydajność każdego nowego użytkownika i przy każdej aktualizacji aplikacji. Optymalizacja na podstawie profilu (PGO) umożliwia aplikacjom optymalizację uruchamiania, zmniejszenie zacinania się interakcji i poprawę ogólnej wydajności środowiska wykonawczego dla użytkowników od pierwszego uruchomienia.
W przypadku profilu podstawowego wszystkie interakcje użytkownika – takie jak uruchamianie aplikacji, przechodzenie między ekranami czy przewijanie treści – są płynniejsze od pierwszego uruchomienia. Zwiększenie szybkości i responsywności aplikacji prowadzi do wzrostu liczby aktywnych użytkowników dziennie i wyższego średniego wskaźnika ponownych wizyt.
Profile podstawowe pomagają w optymalizacji wykraczającej poza uruchamianie aplikacji, ponieważ zawierają typowe interakcje użytkowników, które poprawiają działanie aplikacji od pierwszego uruchomienia. Kompilacja AOT z przewodnikiem nie zależy od urządzeń użytkowników i może być przeprowadzana raz na wersję na komputerze używanym do programowania zamiast na urządzeniu mobilnym. Dzięki dostarczaniu wersji z profilem podstawowym optymalizacje aplikacji są dostępne znacznie szybciej niż w przypadku korzystania tylko z profili w chmurze.
Gdy nie używasz profilu podstawowego, cały kod aplikacji jest kompilowany JIT w pamięci po zinterpretowaniu lub w tle do pliku odex, gdy urządzenie jest bezczynne. Użytkownicy mogą wtedy mieć problemy z działaniem aplikacji po jej zainstalowaniu lub zaktualizowaniu po raz pierwszy, zanim nowe ścieżki zostaną zoptymalizowane.
4. Konfigurowanie modułu generatora profilu podstawowego
Profile podstawowe możesz wygenerować za pomocą klasy testu z instrumentacją, która wymaga dodania do projektu nowego modułu Gradle. Najłatwiej dodać go do projektu za pomocą kreatora modułów Androida Studio, który jest dostępny w Androidzie Studio Hedgehog lub nowszym.
Otwórz okno kreatora nowego modułu, klikając prawym przyciskiem myszy projekt lub moduł w panelu Project i wybierając New > Module (Nowy > Moduł).

W otwartym oknie w panelu Szablony wybierz Generator profilu podstawowego.

Oprócz zwykłych parametrów, takich jak nazwa modułu, nazwa pakietu, język czy język konfiguracji kompilacji, istnieją 2 dane wejściowe, które nie są typowe dla nowego modułu: Aplikacja docelowa i Użyj urządzenia zarządzanego przez Gradle.
Aplikacja docelowa to moduł aplikacji, który służy do generowania profili podstawowych. Jeśli w projekcie masz więcej niż 1 moduł aplikacji, wybierz ten, dla którego chcesz uruchomić generatory.
Pole wyboru Use Gradle Managed Device (Użyj urządzenia zarządzanego przez Gradle) ustawia moduł tak, aby generatory profilu podstawowego były uruchamiane na automatycznie zarządzanych emulatorach Androida. Więcej informacji o urządzeniach zarządzanych przez Gradle znajdziesz w artykule Skalowanie testów za pomocą urządzeń zarządzanych przez Gradle. Jeśli odznaczysz to pole, generatory będą używać dowolnego podłączonego urządzenia.
Po określeniu wszystkich szczegółów nowego modułu kliknij Zakończ, aby przejść do tworzenia modułu.
Zmiany wprowadzone przez kreator modułów
Kreator modułu wprowadza w projekcie kilka zmian.
Dodaje moduł Gradle o nazwie baselineprofile lub nazwie wybranej w kreatorze.
Ten moduł korzysta z wtyczki com.android.test, która informuje Gradle, aby nie uwzględniać go w aplikacji, więc może on zawierać tylko kod testowy lub testy porównawcze. Korzysta też z wtyczki androidx.baselineprofile, która umożliwia automatyczne generowanie profili podstawowych.
Kreator wprowadza też zmiany w wybranym module aplikacji docelowej. W szczególności stosuje wtyczkę androidx.baselineprofile, dodaje zależność androidx.profileinstaller i zależność baselineProfile do nowo utworzonego modułu build.gradle(.kts):
plugins {
id("androidx.baselineprofile")
}
dependencies {
// ...
implementation("androidx.profileinstaller:profileinstaller:1.3.0")
"baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}
Dodanie zależności androidx.profileinstaller umożliwia:
- Lokalnie sprawdź wzrost wydajności wygenerowanych profili podstawowych.
- Używaj profili podstawowych na Androidzie 7 (poziom interfejsu API 24) i Androidzie 8 (poziom interfejsu API 26), które nie obsługują profili w chmurze.
- Korzystanie z profili podstawowych na urządzeniach, które nie mają Usług Google Play.
Zależność baselineProfile(project(":baselineprofile")) informuje Gradle, z którego modułu ma pobrać wygenerowane profile bazowe.
Po skonfigurowaniu projektu napisz klasę generatora profili podstawowych.
5. Napisz generator profilu podstawowego
Zwykle generujesz profile podstawowe dla typowych ścieżek użytkowników w aplikacji.
Kreator modułu tworzy podstawową klasę testową BaselineProfileGenerator, która może generować profil bazowy na potrzeby uruchamiania aplikacji. Wygląda ona tak:
@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun generate() {
rule.collect("com.example.baselineprofiles_codelab") {
// This block defines the app's critical user journey. This is where you
// optimize for app startup. You can also navigate and scroll
// through your most important UI.
// Start default activity for your app.
pressHome()
startActivityAndWait()
// TODO Write more interactions to optimize advanced journeys of your app.
// For example:
// 1. Wait until the content is asynchronously loaded.
// 2. Scroll the feed content.
// 3. Navigate to detail screen.
// Check UiAutomator documentation for more information about how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
}
}
Ta klasa używa BaselineProfileRule reguły testowej i zawiera jedną metodę testową do generowania profilu. Punktem wejścia do generowania profilu jest funkcja collect(). Wymaga tylko 2 parametrów:
packageName: pakiet aplikacji.profileBlock: ostatni parametr funkcji LAMBDA.
W profileBlock lambda określasz interakcje, które obejmują typowe ścieżki użytkowników w aplikacji. Biblioteka uruchamia profileBlock kilka razy, zbiera wywołane klasy i Funkcje oraz generuje na urządzeniu profil podstawowy z kodem do Optymalizacji.
Domyślnie utworzona klasa generatora zawiera interakcje, które uruchamiają domyślny Activity i czekają, aż pierwsza klatka aplikacji zostanie wyrenderowana za pomocą metody startActivityAndWait().
Rozszerzanie generatora za pomocą niestandardowych ścieżek
Wygenerowana klasa zawiera też kilka TODO, które umożliwiają zapisywanie większej liczby interakcji w celu optymalizacji zaawansowanych ścieżek w aplikacji. Jest to zalecane, aby można było optymalizować wydajność poza uruchomieniem aplikacji.
W naszej przykładowej aplikacji możesz zidentyfikować te ścieżki, wykonując te czynności:
- Uruchom aplikację. Jest to już częściowo uwzględnione w wygenerowanej klasie.
- Poczekaj, aż treść zostanie załadowana asynchronicznie.
- Przewiń listę przekąsek.
- Otwórz szczegóły przekąski.
Zmień generator, aby zawierał funkcje opisane w tym fragmencie kodu, które obejmują typowe ścieżki:
// ...
rule.collect("com.example.baselineprofiles_codelab") {
// This block defines the app's critical user journey. This is where you
// optimize for app startup. You can also navigate and scroll
// through your most important UI.
// Start default activity for your app.
pressHome()
startActivityAndWait()
// TODO Write more interactions to optimize advanced journeys of your app.
// For example:
// 1. Wait until the content is asynchronously loaded.
waitForAsyncContent()
// 2. Scroll the feed content.
scrollSnackListJourney()
// 3. Navigate to detail screen.
goToSnackDetailJourney()
// Check UiAutomator documentation for more information about how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
// ...
Teraz opisz interakcje na każdej z wymienionych ścieżek. Możesz napisać ją jako funkcję rozszerzenia MacrobenchmarkScope, aby mieć dostęp do parametrów i funkcji, które udostępnia. Dzięki temu możesz ponownie wykorzystać interakcje z wartościami porównawczymi, aby sprawdzić wzrost skuteczności.
Czekanie na treści asynchroniczne
Wiele aplikacji ma pewien rodzaj asynchronicznego wczytywania podczas uruchamiania, znanego też jako stan pełnego wyświetlania, który informuje system, kiedy treść jest załadowana i wyrenderowana, a użytkownik może z nią wejść w interakcję. Poczekaj na stan w generatorze (waitForAsyncContent) z tymi interakcjami:
- Znajdź listę przekąsek w pliku danych.
- Poczekaj, aż na ekranie pojawi się kilka elementów z listy.
fun MacrobenchmarkScope.waitForAsyncContent() {
device.wait(Until.hasObject(By.res("snack_list")), 5_000)
val contentList = device.findObject(By.res("snack_list"))
// Wait until a snack collection item within the list is rendered.
contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}
Ścieżka przewijanej listy
W przypadku przewijanej listy przekąsek (scrollSnackListJourney) możesz wykonać te działania:
- Znajdź element interfejsu listy przekąsek.
- Ustaw marginesy gestów tak, aby nie aktywować nawigacji systemowej.
- Przewiń listę i poczekaj, aż interfejs się ustabilizuje.
fun MacrobenchmarkScope.scrollSnackListJourney() {
val snackList = device.findObject(By.res("snack_list"))
// Set gesture margin to avoid triggering gesture navigation.
snackList.setGestureMargin(device.displayWidth / 5)
snackList.fling(Direction.DOWN)
device.waitForIdle()
}
Otwórz szczegółową ścieżkę
Ostatnia ścieżka (goToSnackDetailJourney) obejmuje te interakcje:
- Znajdź listę przekąsek i wszystkie przekąski, z którymi możesz pracować.
- Wybierz element z listy.
- Kliknij element i poczekaj, aż wczyta się ekran szczegółów. Możesz wykorzystać fakt, że lista przekąsek nie będzie już widoczna na ekranie.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
val snackList = device.findObject(By.res("snack_list"))
val snacks = snackList.findObjects(By.res("snack_item"))
// Select snack from the list based on running iteration.
val index = (iteration ?: 0) % snacks.size
snacks[index].click()
// Wait until the screen is gone = the detail is shown.
device.wait(Until.gone(By.res("snack_list")), 5_000)
}
Po zdefiniowaniu wszystkich interakcji potrzebnych do uruchomienia generatora profilu podstawowego musisz określić urządzenie, na którym będzie on działać.
6. Przygotowywanie urządzenia do uruchomienia generatora
Aby wygenerować profile podstawowe, zalecamy użycie emulatora, takiego jak urządzenie zarządzane przez Gradle, lub urządzenia z Androidem 13 (API 33) lub nowszym.
Aby proces był powtarzalny i automatycznie generować profile podstawowe, możesz użyć urządzeń zarządzanych przez Gradle. Urządzenia zarządzane przez Gradle umożliwiają przeprowadzanie testów na emulatorze Androida bez konieczności ręcznego uruchamiania i zamykania go. Więcej informacji o urządzeniach zarządzanych przez Gradle znajdziesz w artykule Skalowanie testów za pomocą urządzeń zarządzanych przez Gradle.
Aby zdefiniować urządzenie zarządzane przez Gradle, dodaj jego definicję do pliku :baselineprofile modułu build.gradle.kts, jak pokazano w tym fragmencie kodu:
android {
// ...
testOptions.managedDevices.devices {
create<ManagedVirtualDevice>("pixel6Api31") {
device = "Pixel 6"
apiLevel = 31
systemImageSource = "aosp"
}
}
}
W tym przypadku używamy Androida 11 (poziom interfejsu API 31), a aosp obraz systemu ma dostęp do roota.
Następnie skonfiguruj wtyczkę Gradle profilu podstawowego, aby używała zdefiniowanego urządzenia zarządzanego przez Gradle. Aby to zrobić, dodaj nazwę urządzenia do właściwości managedDevices i wyłącz useConnectedDevices, jak pokazano w tym fragmencie kodu:
android {
// ...
}
baselineProfile {
managedDevices += "pixel6Api31"
useConnectedDevices = false
}
dependencies {
// ...
}
Następnie wygeneruj profil podstawowy.
7. Generowanie profilu podstawowego
Gdy urządzenie będzie gotowe, możesz utworzyć profil podstawowy. Wtyczka Gradle profilu podstawowego tworzy zadania Gradle, aby zautomatyzować cały proces uruchamiania klasy testu generatora i stosowania wygenerowanych profili podstawowych w aplikacji.
Nowy kreator modułów utworzył konfigurację uruchamiania, aby umożliwić szybkie uruchamianie zadania Gradle ze wszystkimi niezbędnymi parametrami bez konieczności przełączania się między terminalem a Androidem Studio.
Aby go uruchomić, znajdź konfigurację uruchamiania Generate Baseline Profile i kliknij przycisk Uruchom
.

Zadanie uruchamia obraz emulatora zdefiniowany wcześniej. Kilka razy uruchom interakcje z BaselineProfileGeneratorklasy testowej, a potem zamknij emulator i przekaż dane wyjściowe do Android Studio.
Gdy generator zakończy działanie, wtyczka Gradle automatycznie umieści wygenerowany plik baseline-prof.txt w aplikacji docelowej (moduł :app) w folderze src/release/generated/baselineProfile/.

(Opcjonalnie) Uruchamianie generatora z wiersza poleceń
Możesz też uruchomić generator z poziomu wiersza poleceń. Możesz użyć zadania utworzonego przez urządzenie zarządzane przez Gradle – :app:generateBaselineProfile. To polecenie uruchamia wszystkie testy w projekcie zdefiniowanym przez zależność baselineProfile(project(:baselineProfile)). Moduł zawiera też testy porównawcze do późniejszej weryfikacji wzrostu wydajności, więc te testy kończą się niepowodzeniem z ostrzeżeniem przed uruchamianiem testów porównawczych na emulatorze.
android .testInstrumentationRunnerArguments .androidx.benchmark.enabledRules=BaselineProfile
Aby to zrobić, możesz odfiltrować wszystkie generatory profili bazowych za pomocą tego argumentu narzędzia do uruchamiania instrumentacji. Wszystkie testy porównawcze zostaną pominięte:
Całe polecenie wygląda tak:
./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Rozpowszechnianie aplikacji za pomocą profili bazowych
Gdy profil podstawowy zostanie wygenerowany i skopiowany do kodu źródłowego aplikacji, utwórz jej wersję produkcyjną w zwykły sposób. Nie musisz robić niczego dodatkowego, aby rozpowszechniać profile podstawowe wśród użytkowników. Są one wybierane przez wtyczkę Androida do obsługi Gradle podczas kompilacji i dołączane do pakietu AAB lub APK. Następnie prześlij kompilację do Google Play.
Gdy użytkownicy zainstalują aplikację lub zaktualizują ją z poprzedniej wersji, zostanie zainstalowany też profil podstawowy, co zapewni lepszą wydajność od pierwszego uruchomienia aplikacji.
W następnym kroku dowiesz się, jak sprawdzić, o ile wzrosła wydajność aplikacji dzięki profilom podstawowym.
8. (Opcjonalnie) Dostosowywanie generowania profili podstawowych
Wtyczka Gradle do profili bazowych zawiera opcje dostosowywania sposobu generowania profili do Twoich konkretnych potrzeb. Możesz zmienić to zachowanie za pomocą bloku konfiguracji baselineProfile { } w skryptach kompilacji.
Blok konfiguracji w module :baselineprofile wpływa na sposób uruchamiania generatorów z możliwością dodawania managedDevices i określania, czy mają to być urządzenia useConnectedDevices czy urządzenia zarządzane przez Gradle.
Blok konfiguracji w :appmodule docelowym decyduje o tym, gdzie profile są zapisywane lub jak są generowane. Możesz zmienić te parametry:
automaticGenerationDuringBuild: jeśli ta opcja jest włączona, możesz wygenerować profil podstawowy podczas tworzenia kompilacji do publikacji. Jest to przydatne podczas tworzenia aplikacji w ramach ciągłej integracji przed jej opublikowaniem.saveInSrc: określa, czy wygenerowane profile bazowe są przechowywane w folderzesrc/. Możesz też otworzyć plik z folderu kompilacji:baselineprofile.baselineProfileOutputDir: określa, gdzie mają być przechowywane wygenerowane profile podstawowe.mergeIntoMain: domyślnie profile podstawowe są generowane dla każdego wariantu kompilacji (wariant usługi i rodzaj kompilacji). Jeśli chcesz scalić wszystkie profile wsrc/main, możesz to zrobić, włączając ten flag.filter: możesz filtrować klasy lub metody, które mają być uwzględniane lub wykluczane z wygenerowanych profili podstawowych. Może to być przydatne dla deweloperów bibliotek, którzy chcą uwzględnić tylko kod z biblioteki.
9. Sprawdzanie poprawy wydajności uruchamiania
Po wygenerowaniu profilu podstawowego i dodaniu go do aplikacji sprawdź, czy ma on oczekiwany wpływ na jej wydajność.
Kreator nowego modułu tworzy klasę testową o nazwie StartupBenchmarks. Zawiera on test porównawczy do pomiaru czasu uruchamiania aplikacji i porównuje go z czasem, gdy aplikacja korzysta z profili podstawowych.
Klasa wygląda tak:
@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun startupCompilationNone() =
benchmark(CompilationMode.None())
@Test
fun startupCompilationBaselineProfiles() =
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
private fun benchmark(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(StartupTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.COLD,
iterations = 10,
setupBlock = {
pressHome()
},
measureBlock = {
startActivityAndWait()
// TODO Add interactions to wait for when your app is fully drawn.
// The app is fully drawn when Activity.reportFullyDrawn is called.
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
// from the AndroidX Activity library.
// Check the UiAutomator documentation for more information on how to
// interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
)
}
}
Korzysta z MacrobenchmarkRule, który może przeprowadzać testy porównawcze aplikacji i zbierać dane o jej wydajności. Punktem wejścia do pisania testu porównawczego jest funkcja measureRepeated z reguły.
Wymaga kilku parametrów:
packageName:aplikację, którą chcesz mierzyć.metrics: rodzaj informacji, które chcesz mierzyć podczas testu porównawczego.iterations: ile razy test porównawczy się powtarza.startupMode: jak ma się uruchamiać aplikacja po rozpoczęciu testu porównawczego.setupBlock: jakie interakcje z aplikacją muszą nastąpić przed pomiarem.measureBlock: interakcje z aplikacją, które chcesz mierzyć podczas testu porównawczego.
Klasa testowa zawiera też 2 testy: startupCompilationeNone() i startupCompilationBaselineProfiles(), które wywołują funkcję benchmark() z różnymi wartościami compilationMode.
CompilationMode
Parametr CompilationMode określa sposób wstępnego kompilowania aplikacji do kodu maszynowego. Dostępne są te opcje:
DEFAULT: częściowo wstępnie kompiluje aplikację za pomocą profili podstawowych, jeśli są dostępne. Jest on używany, jeśli nie zastosowano parametrucompilationMode.None(): resetuje stan kompilacji aplikacji i nie przeprowadza jej wstępnej kompilacji. Podczas wykonywania aplikacji nadal jest włączona kompilacja JIT.Partial(): wstępnie kompiluje aplikację za pomocą profili podstawowych lub uruchomień rozgrzewających albo obu tych metod.Full(): wstępnie kompiluje cały kod aplikacji. Jest to jedyna opcja na Androidzie 6 (API 23) i starszym.
Jeśli chcesz rozpocząć optymalizację wydajności aplikacji, możesz wybrać DEFAULT tryb kompilacji, ponieważ wydajność jest podobna do wydajności aplikacji zainstalowanej z Google Play. Jeśli chcesz porównać korzyści związane z wydajnością, jakie dają profile podstawowe, możesz to zrobić, porównując wyniki trybów kompilacji None i Partial.
Zmodyfikuj test porównawczy, aby czekał na treść
Testy porównawcze są tworzone podobnie jak generatory profili podstawowych – przez zapisywanie interakcji z aplikacją. Domyślnie utworzone testy porównawcze czekają tylko na wyrenderowanie pierwszej klatki (podobnie jak BaselineProfileGenerator), dlatego zalecamy ich ulepszenie, aby czekały na treści asynchroniczne.
Możesz to zrobić, ponownie wykorzystując funkcje rozszerzeń napisane dla generatora. Ten test porównawczy rejestruje czasy uruchamiania – za pomocą StartupTimingMetric() – dlatego zalecamy, aby uwzględniać w nim tylko oczekiwanie na treści asynchroniczne, a następnie utworzyć osobny test porównawczy dla pozostałych ścieżek użytkownika zdefiniowanych w generatorze.
// ...
measureBlock = {
startActivityAndWait()
// The app is fully drawn when Activity.reportFullyDrawn is called.
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
// from the AndroidX Activity library.
waitForAsyncContent() // <------- Added to wait for async content.
// Check the UiAutomator documentation for more information on how to
// interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
Przeprowadź testy porównawcze
Testy porównawcze możesz uruchamiać w taki sam sposób jak testy z instrumentacją. Możesz uruchomić funkcję testową lub całą klasę, klikając ikonę rynny obok niej.

Upewnij się, że wybrane jest urządzenie fizyczne, ponieważ uruchamianie testów porównawczych na emulatorze Androida kończy się niepowodzeniem w czasie działania z ostrzeżeniem, że test może dać nieprawidłowe wyniki. Możesz co prawda uruchomić go na emulatorze, ale mierzysz wtedy wydajność urządzenia hosta. Jeśli jest mocno obciążony, testy porównawcze działają wolniej i odwrotnie.

Po uruchomieniu testu porównawczego aplikacja zostanie ponownie skompilowana, a następnie zostaną uruchomione testy porównawcze. Testy porównawcze uruchamiają, zatrzymują, a nawet ponownie instalują aplikację kilka razy na podstawie zdefiniowanych przez Ciebie iterations.
Po zakończeniu testów porównawczych możesz zobaczyć czasy w danych wyjściowych Android Studio, jak pokazano na zrzucie ekranu poniżej:

Na zrzucie ekranu widać, że czas uruchamiania aplikacji jest różny dla każdego CompilationMode. Wartości mediany są podane w tej tabeli:
timeToInitialDisplay [ms] | timeToFullDisplay [ms] | |
Brak | 202.2 | 818,8 |
BaselineProfiles | 193,7 | 637,9 |
Poprawa | 4% | 28% |
Różnica między trybami kompilacji w przypadku timeToFullDisplay wynosi 180 ms,co oznacza wzrost o ok. 28% dzięki samemu profilowi podstawowemu. CompilationNone działa gorzej, ponieważ podczas uruchamiania aplikacji urządzenie musi wykonać najwięcej kompilacji JIT. CompilationBaselineProfiles działa lepiej, ponieważ kompilacja częściowa z profilami bazowymi AOT kompiluje kod, którego użytkownik najprawdopodobniej będzie używać, a kod niekrytyczny pozostawia nieskompilowany, dzięki czemu nie musi być od razu wczytywany.
10. (Opcjonalnie) Sprawdzanie poprawy wydajności przewijania
Podobnie jak w poprzednim kroku możesz zmierzyć i sprawdzić wydajność przewijania. Najpierw utwórz ScrollBenchmarks klasę testową z regułą testu porównawczego i 2 metodami testowymi, które używają różnych trybów kompilacji:
@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun scrollCompilationNone() = scroll(CompilationMode.None())
@Test
fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())
private fun scroll(compilationMode: CompilationMode) {
// TODO implement
}
}
W metodzie scroll użyj funkcji measureRepeated z wymaganymi parametrami. W przypadku parametru metrics użyj parametru FrameTimingMetric, który mierzy czas potrzebny na wygenerowanie ramek interfejsu:
private fun scroll(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.WARM,
iterations = 10,
setupBlock = {
// TODO implement
},
measureBlock = {
// TODO implement
}
)
}
Tym razem musisz bardziej podzielić interakcje między setupBlock i measureBlock, aby mierzyć tylko czas trwania klatek podczas pierwszego układu i przewijania treści. Dlatego umieść funkcje, które uruchamiają ekran domyślny, w setupBlock, a utworzone już funkcje rozszerzenia – waitForAsyncContent() i scrollSnackListJourney() – w measureBlock:
private fun scroll(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "com.example.baselineprofiles_codelab",
metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.WARM,
iterations = 10,
setupBlock = {
pressHome()
startActivityAndWait()
},
measureBlock = {
waitForAsyncContent()
scrollSnackListJourney()
}
)
}
Gdy test porównawczy będzie gotowy, możesz go uruchomić jak wcześniej, aby uzyskać wyniki widoczne na tym zrzucie ekranu:

Wartość FrameTimingMetric podaje czas trwania klatek w milisekundach (frameDurationCpuMs) przy 50, 90, 95 i 99 percentylu. Na Androidzie 12 (API na poziomie 31) i nowszych wersjach zwraca też czas, o ile klatki przekraczają limit (frameOverrunMs). Wartość może być ujemna, co oznacza, że na wyrenderowanie klatki pozostało więcej czasu.
Z wyników widać, że CompilationBaselineProfiles ma średnio o 2 ms krótszy czas trwania klatki, co może być niezauważalne dla użytkowników. W przypadku pozostałych wartości procentowych wyniki są jednak bardziej oczywiste. W przypadku P99 różnica wynosi 43, 5 ms, co w przypadku urządzenia działającego z częstotliwością 90 FPS oznacza ponad 3 pominięte klatki. Na przykład w przypadku Pixela 6 jest to 1000 ms / 90 klatek na sekundę = ok. 11 ms maksymalnego czasu renderowania klatki.
11. Gratulacje
Gratulujemy ukończenia tego ćwiczenia z programowania i zwiększenia wydajności aplikacji dzięki profilom podstawowym.
Dodatkowe zasoby
Zapoznaj się z tymi dodatkowymi materiałami:
- Sprawdzanie wydajności aplikacji za pomocą Macrobenchmark: ćwiczenia z programowania, które bardziej szczegółowo omawiają testy porównawcze.
- Próbki wydajności: repozytorium zawierające Macrobenchmark i inne próbki wydajności.
- Przykładowa aplikacja Now In Android: rzeczywista aplikacja, która wykorzystuje testy porównawcze i profile podstawowe do zwiększania wydajności.