Instrukcje

Bardziej szczegółowe uwagi dotyczące wydajności

Czas czytania: 8 minut
3 Autorzy
Ben Weiss, Breana Tate, Jossi Wolf

Uspokójcie się i pozwólcie nam przedstawić więcej informacji o skuteczności.

Witamy w 3 dniu Tygodnia wyróżnień za skuteczność. Dziś nadal będziemy udostępniać szczegółowe informacje i wskazówki dotyczące ważnych obszarów wydajności aplikacji. Omówimy optymalizację z użyciem profilowania, ulepszenia wydajności Jetpack Compose i kwestie związane z pracą za kulisami. Zaczynajmy.

Optymalizacja na podstawie profilu

Profile podstawoweprofile uruchamiania mają kluczowe znaczenie dla poprawy wydajności uruchamiania i działania aplikacji na Androida. Są one częścią grupy optymalizacji wydajności o nazwie Optymalizacja na podstawie profilu.

Gdy aplikacja jest pakowana, dekompilator d8 pobiera klasy i metody oraz wypełnia nimi pliki classes.dex aplikacji. Gdy użytkownik otworzy aplikację, te pliki DEX są ładowane jeden po drugim, aż aplikacja będzie mogła się uruchomić. Dzięki podaniu profilu uruchamiania informujesz kompilator d8, które klasy i metody mają być spakowane w pierwszych plikach classes.dex. Dzięki tej strukturze aplikacja wczytuje mniej plików, co z kolei przyspiesza jej uruchamianie.

Profile podstawowe skutecznie przenoszą kroki kompilacji JIT (Just in Time) z urządzeń użytkowników na urządzenia deweloperów. Wygenerowany skompilowany kod AOT (Ahead Of Time) zmniejsza czas uruchamiania i rozwiązywania problemów z renderowaniem.

Trello i profile podstawowe

Zapytaliśmy inżynierów pracujących nad aplikacją Trello, jak profile podstawowe wpłynęły na wydajność ich aplikacji. Po zastosowaniu profili podstawowych w głównej ścieżce użytkownika czas uruchamiania aplikacji Trello skrócił się o 25%.

obraz.png

Dzięki profilom podstawowym firma Trello zdołała skrócić czas uruchamiania aplikacji o 25%.

Profile podstawowe w Meta

Inżynierowie z Mety opublikowali niedawno artykuł o tym, jak przyspieszają działanie aplikacji na Androida za pomocą profili bazowych.

obraz.png

Po zastosowaniu profili podstawowych w aplikacjach Meta różne kluczowe wskaźniki wzrosły nawet o 40%.

Takie ulepszenia techniczne pomagają też zwiększać zadowolenie użytkowników i odnosić sukcesy biznesowe. Udostępnienie tych informacji właścicielom produktów, dyrektorom ds. technicznych i osobom podejmującym decyzje może też przyspieszyć poprawę wydajności aplikacji.

Pierwsze kroki z profilami podstawowymi

Aby wygenerować profil bazowy lub profil uruchomienia, napisz test makrobenchmarkowy, który będzie wykonywać działania w aplikacji. Podczas testu zbierane są dane profilu, które będą używane podczas kompilacji aplikacji. Testy są pisane przy użyciu nowego interfejsu UiAutomator API, o którym opowiemy jutro.

Napisanie takiego benchmarku jest proste. Pełny przykład znajdziesz w GitHubie.

  @Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

co należy wziąć pod uwagę

Zacznij od napisania testów makrobenchmarkowych profilu podstawowego i profilu uruchamiania dla ścieżki najczęściej pokonywanej przez użytkowników. Oznacza to główny punkt wejścia użytkowników do aplikacji, który zwykle następuje po zalogowaniu się. Następnie napisz więcej przypadków testowych, aby uzyskać pełniejszy obraz tylko w przypadku profili podstawowych. Nie musisz obejmować wszystkich elementów profilem podstawowym. Korzystaj z najczęściej używanych ścieżek i mierz skuteczność w terenie. Więcej na ten temat znajdziesz w jutrzejszym poście.

Pierwsze kroki z optymalizacją na podstawie profilu

Aby dowiedzieć się, jak działają profile podstawowe, obejrzyj ten film z Android Developers Summit:

Aby dowiedzieć się więcej, obejrzyj odcinek Android Build Time poświęcony optymalizacji opartej na profilu: 

Mamy też obszerne przewodniki dotyczące profili podstawowychprofili uruchamiania.

Ulepszenia wydajności Jetpack Compose

Inwestycje zespołu inżynierów w wydajność platformy interfejsu Androida przyniosły efekty. Od wersji 1.9 Jetpack Compose w wewnętrznym teście długiego przewijania liczba zacięć podczas przewijania spadła do 0, 2%.

jankyFrames.png

Te ulepszenia były możliwe dzięki kilku funkcjom wprowadzonym w najnowszych wersjach.

Okres ważności pamięci podręcznej z możliwością dostosowania

Domyślnie leniwe układy komponują tylko 1 element z wyprzedzeniem w kierunku przewijania, a po przewinięciu elementu poza ekran jest on odrzucany. Możesz teraz dostosować liczbę elementów do zachowania za pomocą ułamka widocznego obszaru lub rozmiaru dp. Dzięki temu aplikacja może wykonać więcej pracy z wyprzedzeniem, a po włączeniu wstrzymywalnej kompozycji między klatkami wydajniej wykorzystywać dostępny czas.

Aby zacząć korzystać z dostosowywanych okien pamięci podręcznej, utwórz instancję LazyLayoutCacheWindow i przekaż ją do leniwej listy lub leniwej siatki. Mierz skuteczność aplikacji przy użyciu różnych rozmiarów okna pamięci podręcznej, np. 50% widocznego obszaru. Optymalna wartość zależy od struktury treści i rozmiaru elementu.

  val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)

val state = rememberLazyListState(cacheWindow = dpCacheWindow)

LazyColumn(state = state) {

    // column contents

}

Kompozycja z możliwością wstrzymania

Ta funkcja umożliwia wstrzymywanie kompozycji i dzielenie pracy na kilka klatek. Interfejsy API zostały wprowadzone w wersji 1.9, a w wersji 1.10 są domyślnie używane w przypadku wstępnego pobierania leniwego układu. Największe korzyści odniesiesz w przypadku złożonych elementów o dłuższym czasie tworzenia. 

obraz.png

Więcej optymalizacji wydajności w Compose

W wersjach 1.9 i 1.10 Compose zespół wprowadził też kilka mniej oczywistych optymalizacji.

Ulepszyliśmy kilka interfejsów API, które w tle korzystają z korutyn. Na przykład w przypadku korzystania z DraggableClickable deweloperzy powinni zauważyć krótszy czas reakcji i większą liczbę przydzielonych zasobów.

Optymalizacje śledzenia prostokąta układu poprawiły skuteczność modyfikatorów, takich jak onVisibilityChanged()onLayoutRectChanged(). Przyspiesza to fazę układu, nawet jeśli nie używasz tych interfejsów API.

Kolejnym ulepszeniem wydajności jest używanie wartości z pamięci podręcznej podczas obserwowania pozycji za pomocą funkcji onPlaced().

Wstępne pobieranie tekstu w tle

Od wersji 1.9 Compose umożliwia wstępne pobieranie tekstu w wątku w tle. Umożliwia to wstępne wypełnianie pamięci podręcznych, aby przyspieszyć układ tekstu, co ma znaczenie dla wydajności renderowania aplikacji. Podczas tworzenia układu tekst musi zostać przekazany do platformy Android, gdzie jest wypełniana pamięć podręczna słów. Domyślnie działa on w wątku interfejsu. Przeniesienie wstępnego pobierania i wypełniania pamięci podręcznej słów do wątku w tle może przyspieszyć układ, zwłaszcza w przypadku dłuższych tekstów. Aby pobierać z wyprzedzeniem w wątku w tle, możesz przekazać niestandardowy moduł wykonawczy do dowolnej funkcji kompozycyjnej, która korzysta z funkcji BasicText, przekazując LocalBackgroundTextMeasurementExecutor do CompositionLocalProvider w ten sposób:

  val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

    BasicText("Some text that should be measured on a background thread!")


}

W zależności od tekstu może to zwiększyć wydajność renderowania tekstu. Aby mieć pewność, że poprawia ona wydajność renderowania aplikacji, przeprowadź test porównawczy i porównaj wyniki.

Uwagi dotyczące wydajności pracy w tle

Praca w tle jest kluczowym elementem wielu aplikacji. Do wykonywania takich zadań jak:

  • okresowe przesyłanie zdarzeń analitycznych,
  • synchronizowanie danych między usługą backendu a bazą danych.
  • przetwarzanie multimediów (np. zmiana rozmiaru lub kompresja obrazów);

Kluczowym wyzwaniem podczas wykonywania tych zadań jest zachowanie równowagi między wydajnością a efektywnością energetyczną. WorkManager pozwala osiągnąć tę równowagę. Został on zaprojektowany tak, aby oszczędzać energię i umożliwiać odkładanie pracy na optymalny okres wykonania, na który wpływa wiele czynników, w tym ograniczenia określone przez Ciebie lub narzucone przez system. 

WorkManager nie jest jednak uniwersalnym rozwiązaniem. Android ma też kilka interfejsów API zoptymalizowanych pod kątem zużycia energii, które zostały zaprojektowane specjalnie z myślą o określonych typowych głównych ścieżkach użytkownika (CUJ).  

Na stronie docelowej dotyczącej pracy w tle znajdziesz listę niektórych z tych działań,  w tym aktualizowanie widżetu i pobieranie lokalizacji w tle.

Narzędzia do debugowania lokalnego w przypadku pracy w tle: typowe scenariusze

Aby debugować pracę w tle i dowiedzieć się, dlaczego zadanie mogło zostać opóźnione lub nie powiodło się, musisz mieć wgląd w to, jak system zaplanował Twoje zadania. 

Aby Ci w tym pomóc, WorkManager udostępnia kilka powiązanych narzędzi do debugowania lokalnego i optymalizacji wydajności (niektóre z nich działają też w przypadku JobScheduler). Oto kilka typowych scenariuszy, które mogą wystąpić podczas korzystania z WorkManagera, oraz wyjaśnienie narzędzi, których możesz użyć do debugowania.

Rozwiązywanie problemów z wykonywaniem zaplanowanej pracy

Opóźnienie lub brak wykonania zaplanowanej pracy może wynikać z różnych czynników, w tym z niespełnienia określonych ograniczeń lub z ograniczeń nałożonych przez system

Pierwszym krokiem w sprawdzaniu, dlaczego zaplanowane zadanie nie jest wykonywane, jest potwierdzenie, że zostało ono prawidłowo zaplanowane. Po potwierdzeniu stanu harmonogramu sprawdź, czy istnieją jakieś niespełnione ograniczenia lub warunki wstępne, które uniemożliwiają wykonanie zadania.

Istnieje kilka narzędzi do debugowania w tym scenariuszu.

Background Task Inspector

Background Task Inspector to zaawansowane narzędzie zintegrowane bezpośrednio z Androidem Studio. Zawiera wizualną reprezentację wszystkich zadań WorkManagera i ich stanów (Uruchomione, W kolejce, Nieudane, Zakończone). 

Aby debugować, dlaczego zaplanowane zadanie nie jest wykonywane za pomocą Background Task Inspector, zapoznaj się z wymienionymi stanami zadania. Stan „W kolejce” oznacza, że zadanie zostało zaplanowane, ale nadal czeka na uruchomienie.

Korzyści: to narzędzie nie tylko ułatwia przeglądanie wszystkich zadań, ale jest też szczególnie przydatne, jeśli masz połączone działania. Inspektor zadań w tle oferuje widok wykresu, który może wizualizować, czy niepowodzenie poprzedniego zadania mogło wpłynąć na wykonanie następnego zadania.

obraz.png

Widok listy w narzędziu Background Task Inspector

obraz.png

Widok wykresu w inspektorze zadań w tle

adb shell dumpsys jobscheduler

To polecenie zwraca listę wszystkich aktywnych zadań JobScheduler (w tym elementów Worker biblioteki WorkManager) wraz z określonymi ograniczeniami i ograniczeniami narzuconymi przez system. Zwraca też historię zadań. 

Użyj tej opcji, jeśli chcesz wyświetlić zaplanowaną pracę i powiązane z nią ograniczenia w inny sposób. W przypadku wersji WorkManagera starszych niż WorkManager 2.10.0 funkcja adb shell dumpsys jobscheduler zwróci listę elementów Worker o tej nazwie:

  [package name]/androidx.work.impl.background.systemjob.SystemJobService

Jeśli Twoja aplikacja ma wielu procesów roboczych, aktualizacja do WorkManager 2.10.0 umożliwi Ci wyświetlanie ich nazw i łatwe rozróżnianie:

  #WorkerName#@[package name]/androidx.work.impl.background.systemjob.SystemJobService

Zalety:  to polecenie jest przydatne, gdy chcesz sprawdzić, czy występowały jakieś ograniczenia narzucone przez system , których nie można określić za pomocą Background Task Inspector. Na przykład zwróci to zasobnik rezerwowy aplikacji, który może wpływać na okres, w którym zaplanowane zadania zostaną wykonane.

Włącz logowanie debugowania

Możesz włączyć logowanie niestandardowe, aby wyświetlać szczegółowe logi WorkManagera z dołączonymi informacjami WM—

Korzyści: dzięki temu możesz sprawdzić, kiedy zaplanowano pracę, czy spełnione są ograniczenia i jakie są zdarzenia cyklu życia. Podczas tworzenia aplikacji możesz korzystać z tych logów.

WorkInfo.StopReason

Jeśli zauważysz nieprzewidywalną skuteczność konkretnego pracownika, możesz programowo sprawdzić, dlaczego został on zatrzymany podczas poprzedniej próby uruchomienia, korzystając z funkcji WorkInfo.getStopReason

Warto skonfigurować aplikację tak, aby obserwowała WorkInfo za pomocą getWorkInfoByIdFlow i sprawdzała, czy na jej działanie mają wpływ ograniczenia dotyczące działania w tle, ograniczenia, częste przekroczenia limitu czasu czy nawet zatrzymanie przez użytkownika.

Korzyści: możesz używać WorkInfo.StopReason do zbierania danych z terenu o skuteczności pracowników.

Debugowanie długiego czasu blokady uśpienia przypisanego do WorkManagera, na który zwraca uwagę Android Vitals

Android Vitals zawiera wskaźnik zbyt wielu częściowych blokad uśpienia, który wskazuje blokady uśpienia przyczyniające się do szybkiego zużycia baterii. Może Cię zaskoczyć fakt, że WorkManager uzyskuje blokady wybudzania, aby wykonywać zadania, a jeśli przekroczą one próg ustawiony przez Google Play, może to wpłynąć na widoczność Twojej aplikacji. Jak możesz debugować, dlaczego tak długo trwa blokada uśpienia związana z Twoją pracą? Możesz skorzystać z tych narzędzi:

Panel Android Vitals

Najpierw sprawdź na panelu Android Vitals dotyczących nadmiernego czasu blokady uśpienia, czy długi czas blokady uśpienia pochodzi z WorkManagera, a nie z alarmu lub innej blokady uśpienia. W dokumentacji Identyfikowanie blokad uśpienia utworzonych przez inne interfejsy API znajdziesz informacje o tym, które blokady uśpienia są utrzymywane z powodu WorkManagera. 

Perfetto

Perfetto to narzędzie do analizowania śladów systemowych. Jeśli używasz go do debugowania WorkManagera, możesz wyświetlić sekcję „Stan urządzenia”, aby sprawdzić, kiedy rozpoczęło się zadanie, jak długo trwało i jaki ma wpływ na zużycie energii. 

W sekcji „Stan urządzenia: zadania” możesz śledzić wykonane zadania i powiązane z nimi blokady wybudzania.

deviceState.png

Sekcja Stan urządzenia w Perfetto pokazująca wykonanie CleanupWorker i BlurWorker.

Zasoby

Na stronie Debugowanie WorkManagera znajdziesz omówienie dostępnych metod debugowania w innych scenariuszach, które mogą Ci się przytrafić.

Aby wypróbować niektóre z tych metod i dowiedzieć się więcej o debugowaniu biblioteki WorkManager, zapoznaj się z ćwiczeniami z programowania Zaawansowane funkcje biblioteki WorkManager i testowanie.

Dalsze kroki

Dziś poszliśmy o krok dalej i zbadaliśmy, jak środowisko wykonawcze Androida i Jetpack Compose renderują aplikację. Niezależnie od tego, czy chodzi o wstępną kompilację ścieżek krytycznych za pomocą profili podstawowych, czy o wygładzanie stanów przewijania za pomocą nowych funkcji Compose 1.9 i 1.10, te narzędzia koncentrują się na wrażeniach związanych z aplikacją. Przyjrzeliśmy się też szczegółowo sprawdzonym metodom debugowania pracy w tle.

Zapytaj Androida

W piątek organizujemy sesję pytań i odpowiedzi na żywo na temat skuteczności. Zadaj teraz pytania, używając hashtagu #AskAndroid, a eksperci na nie odpowiedzą.

Wyzwanie

W poniedziałek zachęcaliśmy Cię do włączenia R8. Dziś prosimy Cię o wygenerowanie jednego profilu podstawowego dla Twojej aplikacji.

Dzięki Android Studio Otter kreator modułu generatora profili podstawowych ułatwia to zadanie. Wybierz główną ścieżkę użytkownika, nawet jeśli jest to tylko uruchomienie aplikacji i logowanie, i wygeneruj profil.

Gdy już go zdobędziesz, uruchom Macrobenchmark, aby porównać CompilationMode.NoneCompilationMode.Partial.

Podziel się informacjami o skróceniu czasu uruchamiania w mediach społecznościowych, używając hashtagu #optimizationEnabled.

Oglądaj jutro

Zmniejszono rozmiar aplikacji za pomocą R8 i zoptymalizowano środowisko wykonawcze za pomocą optymalizacji opartej na profilu. Jak jednak udowodnić te sukcesy osobom zainteresowanym? Jak wykrywać regresje, zanim trafią do wersji produkcyjnej?

Dołącz do nas jutro na dzień 4: Przewodnik po wyrównywaniu wydajności, w którym pokażemy, jak mierzyć sukces – od danych z terenu w Play Vitals po szczegółowe śledzenie lokalne za pomocą Perfetto.

Autor:

Czytaj dalej