Użytkownicy oczekują, że aplikacje będą się szybko wczytywać i będą działać bez opóźnień. Aplikacja, która uruchamia się powoli, nie spełnia tych oczekiwań i może zniechęcić użytkowników. Takie problemy mogą spowodować, że użytkownik wystawi Twojej aplikacji niską ocenę w Sklepie Play lub całkowicie z niej zrezygnuje.
Na tej stronie znajdziesz informacje, które pomogą Ci zoptymalizować czas uruchamiania aplikacji. Obejmują one omówienie wewnętrznych procesów uruchamiania, profilowanie wydajności uruchamiania oraz typowe problemy z czasem uruchamiania wraz z poradami dotyczącymi ich rozwiązywania.
Informacje o różnych stanach uruchamiania aplikacji
Uruchomienie aplikacji może nastąpić w jednym z 3 stanów: uruchomienie „na zimno”, uruchamianie częściowo z pamięci lub uruchamianie z pamięci. Każdy stan wpływa na to, jak długo trwa, zanim aplikacja stanie się widoczna dla użytkownika. W przypadku uruchomienia „na zimno” aplikacja jest uruchamiana od zera. W pozostałych stanach system musi przenieść działającą aplikację z tła na pierwszy plan.
Zalecamy, aby zawsze przeprowadzać optymalizację na podstawie założenia o uruchomieniu „na zimno”. Może to też poprawić wydajność ciepłych i gorących startów.
Aby zoptymalizować aplikację pod kątem szybkiego uruchamiania, warto zrozumieć, co dzieje się na poziomie systemu i aplikacji oraz jak te 2 elementy wchodzą ze sobą w interakcje w każdym z tych stanów.
Dwa ważne wskaźniki określające uruchamianie aplikacji to czas do początkowego wyświetlenia (TTID) i czas do pełnego wyświetlenia (TTFD). TTID to czas potrzebny do wyświetlenia pierwszej klatki, a TTFD to czas, po którym aplikacja staje się w pełni interaktywna. Obie te wartości są równie ważne, ponieważ TTID informuje użytkownika, że aplikacja się wczytuje, a TTFD oznacza, że aplikacja jest już gotowa do użycia. Jeśli któryś z tych czasów jest zbyt długi, użytkownik może zamknąć aplikację, zanim się ona w pełni załaduje.
Uruchomienie „na zimno”
Uruchomienie „na zimno” oznacza uruchomienie aplikacji od zera. Oznacza to, że do tego momentu proces systemowy tworzy proces aplikacji. Uruchomienie „na zimno” następuje na przykład wtedy, gdy aplikacja jest uruchamiana po raz pierwszy od momentu włączenia urządzenia lub od momentu, gdy system ją zamknął.
Ten typ stanowi największe wyzwanie pod względem skrócenia czasu uruchomienia, bo zarówno system, jak i aplikacja muszą przeprowadzić więcej procesów niż w przypadku innych stanów uruchomienia.
Na początku uruchomienia „na zimno” system wykonuje te 3 zadania:
- Załaduj i uruchom aplikację.
- Wyświetlaj puste okno początkowe aplikacji natychmiast po jej uruchomieniu.
- Utwórz proces aplikacji.
Gdy system utworzy proces aplikacji, będzie on odpowiedzialny za kolejne etapy:
- Utwórz obiekt aplikacji.
- Uruchom wątek główny.
- Utwórz główną aktywność.
- Zainicjuj interfejs.
- Narysuj interfejs na ekranie.
Gdy proces aplikacji zakończy pierwsze rysowanie, proces systemowy zamieni wyświetlane okno tła, zastępując je głównym działaniem. W tym momencie użytkownik może zacząć korzystać z aplikacji.
Więcej informacji o fazach układu w Compose znajdziesz w artykule Fazy Jetpack Compose.
Problemy z wydajnością mogą wystąpić podczas tworzenia aplikacji i aktywności hosta.
Tworzenie aplikacji
Po uruchomieniu aplikacji puste okno początkowe pozostaje na ekranie, dopóki system nie zakończy pierwszego rysowania aplikacji. W tym momencie proces systemowy zamienia okno startowe na okno aplikacji, umożliwiając użytkownikowi korzystanie z niej.
Jeśli zastąpisz metodę Application.onCreate we własnej aplikacji, system wywoła w obiekcie aplikacji metodę onCreate. Następnie aplikacja tworzy wątek główny, zwany też wątkiem UI, i przypisuje mu zadanie utworzenia aktywności hosta aplikacji.
Od tego momentu procesy na poziomie systemu i aplikacji przebiegają zgodnie z etapami cyklu życia aplikacji.
Tworzenie aktywności
Po utworzeniu aktywności przez proces aplikacji wykonuje ona te operacje:
- Inicjuje wartości.
- Wywołuje konstruktory.
- Wywołuje metodę wywołania zwrotnego, np.
Activity.onCreate, odpowiednią do bieżącego stanu cyklu życia aktywności.
Zwykle największy wpływ na czas wczytywania ma metoda onCreate. W aplikacji Jetpack Compose obciążenie metody onCreate często wynika z początkowej kompozycji. Dzieje się tak, gdy aplikacja wywołuje funkcję setContent i wywołuje kompozycje najwyższego poziomu. Szczegółowa lub złożona hierarchia interfejsu lub funkcje kompozycyjne, które wykonują złożone obliczenia w głównym wątku, mogą wydłużyć ten czas.
Uruchamianie częściowo z pamięci
Uruchamianie częściowo z pamięci obejmuje podzbiór operacji, które mają miejsce podczas uruchomienia „na zimno”. Jednocześnie wiąże się to z większym obciążeniem niż uruchomienie z pamięci. Istnieje wiele potencjalnych stanów, które można uznać za ciepły start, np.:
Użytkownik wychodzi z aplikacji, ale potem ją ponownie uruchamia. Proces może być kontynuowany, ale aplikacja musi odtworzyć aktywność od zera, używając wywołania
onCreate.System usuwa aplikację z pamięci, a użytkownik uruchamia ją ponownie. Proces i aktywność muszą zostać ponownie uruchomione, ale zadanie może w pewnym stopniu skorzystać z zapisanego pakietu stanu instancji przekazanego do
onCreate.
Uruchamianie z pamięci
Uruchomienie aplikacji z pamięci ma mniejszy narzut niż uruchomienie „na zimno”. W przypadku uruchamiania z pamięci system przenosi aktywność hosta aplikacji na pierwszy plan. Jeśli cały interfejs aplikacji nadal znajduje się w pamięci, aplikacja może uniknąć powtarzania inicjowania obiektów, interfejsu i renderowania.
Jeśli jednak część pamięci zostanie usunięta trwale w odpowiedzi na zdarzenia przycinania pamięci, takie jak onTrimMemory, te obiekty trzeba będzie odtworzyć w odpowiedzi na zdarzenie uruchamiania z pamięci.
Uruchamianie z pamięci wygląda tak samo jak uruchamianie na zimno. Proces systemowy wyświetla pusty ekran, dopóki aplikacja nie zakończy renderowania aktywności.
Jak zidentyfikować uruchomienie aplikacji w Perfetto
Aby rozwiązać problemy z uruchamianiem aplikacji, warto określić, co dokładnie obejmuje faza uruchamiania. Aby zidentyfikować cały etap uruchamiania aplikacji w Perfetto, wykonaj te czynności:
W Perfetto znajdź wiersz z pochodną wartością „Uruchomienia aplikacji na Androida”. Jeśli nie widzisz tej opcji, spróbuj zarejestrować ślad za pomocą aplikacji do śledzenia systemu na urządzeniu.
Rysunek 2. W Perfetto możesz znaleźć wyliczony fragment danych „Uruchamianie aplikacji na Androida”. Kliknij powiązany wycinek i naciśnij m, aby go wybrać. Nawiasy wokół wycinka oznaczają, ile czasu zajęło wykonanie danego zadania. Czas trwania jest też widoczny na karcie Bieżące zaznaczenie.
Przypnij wiersz Uruchamianie aplikacji na Androida, klikając ikonę pinezki, która jest widoczna, gdy umieścisz wskaźnik myszy nad wierszem.
Przewiń do wiersza z odpowiednią aplikacją i kliknij pierwszą komórkę, aby rozwinąć wiersz.
Powiększ główny wątek (zwykle u góry), naciskając w (aby pomniejszyć, przesunąć w lewo lub w prawo, naciśnij odpowiednio s, a, d).
Rysunek 3. Wartość pochodna Uruchomienia aplikacji na Androida obok głównego wątku aplikacji. Sekcja danych pochodnych ułatwia sprawdzenie, co dokładnie jest uwzględnione w procesie uruchamiania aplikacji, dzięki czemu możesz kontynuować szczegółowe debugowanie.
Podczas analizowania aplikacji Jetpack Compose możesz użyć śledzenia kompozycji, aby wyświetlić szczegółowe informacje o skuteczności interfejsu.
Zwróć szczególną uwagę na sekcje śledzenia w głównym wątku, które zaczynają się od znaku Choreographer#doFrame. Poszukaj wycinków takich jak Compose:recompose, Compose:layout i Compose:draw. Pokazują one, ile czasu aplikacja poświęca na tworzenie, pomiar i umieszczanie oraz rysowanie poszczególnych komponentów.
Korzystanie z danych do sprawdzania i ulepszania startupów
Aby prawidłowo zdiagnozować wydajność czasu uruchamiania, możesz śledzić dane, które pokazują, ile czasu zajmuje uruchomienie aplikacji. Android udostępnia kilka sposobów informowania o problemach z aplikacją i pomaga w ich diagnozowaniu. Android Vitals może Cię powiadomić o wystąpieniu problemu, a narzędzia diagnostyczne pomogą go zdiagnozować.
Korzyści z korzystania z danych dotyczących startupów
Android używa danych czas do pierwszego wyświetlenia (TTID) i czas do pełnego wyświetlenia (TTFD), aby optymalizować uruchamianie aplikacji „na zimno” i „na ciepło”. Środowisko wykonawcze Androida (ART) wykorzystuje dane z tych pomiarów do wydajnego wstępnego kompilowania kodu w celu optymalizacji przyszłych uruchomień.
Szybsze uruchamianie aplikacji prowadzi do bardziej trwałej interakcji użytkowników z aplikacją, co zmniejsza liczbę przypadków wczesnego zamykania, ponownego uruchamiania lub przechodzenia do innej aplikacji.
Android Vitals
Android Vitals może pomóc Ci poprawić działanie aplikacji, wysyłając alert w Konsoli Play za każdym razem, gdy czas uruchamiania aplikacji jest zbyt długi.
Android Vitals uznaje za nadmierne te czasy uruchamiania aplikacji:
- Uruchomienie „na zimno” trwa co najmniej 5 sekund.
- Uruchomienie „na ciepło” trwa co najmniej 2 sekundy.
- Uruchomienie „na ciepło” trwa co najmniej 1,5 sekundy.
Android Vitals korzysta z danych czas do pierwszego wyświetlenia (TTID). Więcej informacji o tym, jak Google Play zbiera dane Android Vitals, znajdziesz w dokumentacji Konsoli Play.
Czas do początkowego wyświetlenia
Czas do początkowego wyświetlenia to czas potrzebny na wyświetlenie pierwszej klatki interfejsu aplikacji. Ta wartość to czas, jaki upływa od uruchomienia aplikacji do wyświetlenia pierwszej klatki, w tym inicjowanie procesu podczas uruchomienia „na zimno”, tworzenie aktywności podczas uruchomienia „na zimno” lub uruchamiania częściowo z pamięci oraz wyświetlanie pierwszej klatki. Niski czas TTID aplikacji poprawia wrażenia użytkowników, ponieważ aplikacja uruchamia się szybko. Identyfikator TTID jest automatycznie zgłaszany w przypadku każdej aplikacji przez platformę Android. Podczas optymalizacji pod kątem uruchamiania aplikacji zalecamy wdrożenie reportFullyDrawn, aby uzyskać informacje o TTFD.
TTID jest mierzony jako wartość czasu, która reprezentuje całkowity czas, jaki upłynął od wystąpienia następujących zdarzeń:
- Uruchamianie procesu.
- Inicjowanie obiektów.
- Tworzenie i inicjowanie aktywności hosta.
- Inicjowanie interfejsu.
- Rysowanie aplikacji po raz pierwszy.
Pobieranie identyfikatora TTID
Aby znaleźć identyfikator TTID, wyszukaj w narzędziu wiersza poleceń Logcat wiersz wyjściowy zawierający wartość o nazwie Displayed. Ta wartość to TTID. Wygląda ona podobnie do przykładu poniżej, w którym TTID wynosi 3s534ms:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
Aby znaleźć TTID w Android Studio, wyłącz filtry w widoku Logcat w menu filtrów, a następnie znajdź czas Displayed, jak pokazano na rysunku 4.
Wyłączenie filtrów jest konieczne, ponieważ ten dziennik jest obsługiwany przez serwer systemowy, a nie przez samą aplikację.
Displayed w Logcat.Wartość Displayed w danych wyjściowych Logcat nie musi odzwierciedlać czasu, jaki upłynął do momentu załadowania i wyświetlenia wszystkich zasobów. Nie uwzględnia zasobów, do których nie odwołuje się początkowa kompozycja (np. danych lub obrazów ładowanych asynchronicznie) ani zasobów tworzonych przez aplikację w ramach inicjowania obiektu. Wyklucza te zasoby, ponieważ ich wczytywanie jest procesem wbudowanym i nie blokuje początkowego wyświetlania aplikacji.
Więcej informacji o zasobach znajdziesz w artykule Zasoby w Compose.
Czasami wiersz Displayed w danych wyjściowych Logcat zawiera dodatkowe pole z całkowitym czasem, jak w tym przykładzie:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)
W takim przypadku pomiar czasu pierwszego wyświetlenia dotyczy tylko aktywności, która jest rysowana jako pierwsza, czyli zwykle aktywności hosta. Pomiar czasu total rozpoczyna się od uruchomienia procesu aplikacji i może obejmować inną aktywność, która została rozpoczęta jako pierwsza, ale nie wyświetla niczego na ekranie. Pomiar czasu total jest wyświetlany tylko wtedy, gdy występuje różnica między czasem pojedynczej aktywności a łącznym czasem uruchamiania.
Zalecamy używanie Logcat w Androidzie Studio, ale jeśli nie korzystasz z Androida Studio, możesz też zmierzyć TTID, uruchamiając aplikację za pomocą adbpolecenia menedżera aktywności w powłoce. Oto przykład:
adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
Dane Displayed pojawią się w danych wyjściowych Logcat tak jak wcześniej. W oknie terminala
wyświetli się:
Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete
Argumenty -c i -a są opcjonalne i umożliwiają określenie wartości <category> i <action>.
Czas do pełnego wyświetlenia
Czas do pełnego wyświetlenia (TTFD) to czas, po którym aplikacja staje się interaktywna dla użytkownika. Jest to czas potrzebny na wyświetlenie pierwszej klatki interfejsu aplikacji, a także treści, które wczytują się asynchronicznie po wyświetleniu pierwszej klatki. Zwykle są to główne treści wczytywane z sieci lub dysku, zgodnie z informacjami podanymi przez aplikację. Innymi słowy, TTFD obejmuje TTID oraz czas potrzebny na to, aby aplikacja stała się użyteczna. Niski czas do pierwszego wyświetlenia (TTFD) poprawia komfort korzystania z aplikacji, ponieważ umożliwia użytkownikom szybkie wchodzenie z nią w interakcję.
Chociaż system może określić TTID, gdy okno hosta renderuje początkową ramkę, nie może automatycznie określić TTFD. Aplikacje często wczytują główne treści asynchronicznie, więc system nie wie, kiedy aplikacja jest w pełni gotowa do użycia przez użytkownika. Aby określić TTFD, aplikacja musi wysyłać do systemu sygnał, gdy osiągnie stan pełnego wyrenderowania.
Pobieranie TTFD
Aby znaleźć TTFD, zasygnalizuj w pełni narysowany stan, wywołując metodę
reportFullyDrawn interfejsu ComponentActivity. Metoda reportFullyDrawn informuje, kiedy aplikacja jest w pełni narysowana i gotowa do użycia. TTFD to czas, który upłynął od momentu otrzymania przez system intencji uruchomienia aplikacji do momentu wywołania funkcji reportFullyDrawn. Jeśli nie wywołasz funkcji
reportFullyDrawn, nie zostanie zgłoszona żadna wartość TTFD.
Aby zmierzyć TTFD, wywołaj funkcję reportFullyDrawn po całkowitym narysowaniu interfejsu i wszystkich danych. Nie wywołuj funkcji reportFullyDrawn, zanim nie zostanie narysowane i wyświetlone pierwsze okno aktywności (zgodnie z pomiarami systemu), ponieważ w takim przypadku system zgłosi czas zmierzony przez system. Inaczej mówiąc, jeśli wywołasz funkcję reportFullyDrawn, zanim system wykryje TTID, system zgłosi zarówno TTID, jak i TTFD jako tę samą wartość, która będzie wartością TTID.
Gdy użyjesz reportFullyDrawn, Logcat wyświetli dane wyjściowe podobne do poniższego przykładu, w którym TTFD wynosi 1 s 54 ms:
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
Dane wyjściowe Logcat czasami zawierają total czas, jak opisano w sekcji Czas do pierwszego wyświetlenia.
Jeśli czas wyświetlania jest dłuższy niż oczekiwany, możesz spróbować zidentyfikować wąskie gardła w procesie uruchamiania.
W podstawowych przypadkach, gdy wiesz, że stan pełnego wyrenderowania został osiągnięty, możesz użyć reportFullyDrawn, aby to zasygnalizować. W przypadku jednak, gdy wątki w tle muszą wykonać pracę w tle, zanim zostanie osiągnięty stan pełnego wyrenderowania, musisz opóźnić reportFullyDrawn, aby uzyskać dokładniejszy pomiar TTFD. Aby dowiedzieć się, jak opóźnić reportFullyDrawn, zapoznaj się z następną sekcją.
Poprawianie dokładności czasu uruchamiania
Jeśli Twoja aplikacja wykonuje leniwe ładowanie, a początkowe wyświetlanie nie obejmuje wszystkich zasobów, np. gdy aplikacja pobiera obrazy z sieci, możesz opóźnić wywołanie funkcji reportFullyDrawn do momentu, gdy aplikacja stanie się użyteczna. Dzięki temu możesz uwzględnić wypełnianie listy w czasie testu porównawczego.
Jeśli na przykład interfejs zawiera listę dynamiczną, np. LazyColumn lub LazyRow, może ona być wypełniana przez zadanie w tle, które kończy się po pierwszym narysowaniu listy, a więc po oznaczeniu interfejsu jako w pełni narysowanego. W takich przypadkach zapełnianie listy nie jest uwzględniane w analizie porównawczej.
Aby uwzględnić zapełnianie listy w czasie testu porównawczego, uzyskaj FullyDrawnReporter za pomocą fullyDrawnReporter i dodaj do niego w kodzie aplikacji moduł raportujący. Zwolnij reportera po zakończeniu wypełniania listy przez zadanie w tle.
FullyDrawnReporter nie wywołuje metody reportFullyDrawn, dopóki wszyscy dodani reporterzy nie zostaną zwolnieni. Dodając moduł raportujący, ale nie udostępniając go, dopóki nie zakończy się proces w tle, masz pewność, że dane o czasie uruchamiania obejmują czas wypełniania listy, bez zmiany zachowania aplikacji dla użytkownika. reportFullyDrawn jest wywoływana dopiero po wykonaniu wszystkich zadań, niezależnie od kolejności.
Jeśli Twoja aplikacja korzysta z Jetpack Compose, możesz użyć tych interfejsów API, aby wskazać stan pełnego wyrenderowania:
ReportDrawn: oznacza, że komponent jest od razu gotowy do interakcji.ReportDrawnWhen: przyjmuje predykat, np.list.count > 0, aby wskazać, kiedy komponent jest gotowy do interakcji.ReportDrawnAfter: przyjmuje metodę zawieszającą, która po zakończeniu wskazuje, że komponent jest gotowy do interakcji.
Poniższy przykład pokazuje, jak można uruchomić jednocześnie wiele zadań w tle, z których każde rejestruje własny moduł raportujący:
class MainActivity : ComponentActivity() {
sealed interface ActivityState {
data object LOADING : ActivityState
data object LOADED : ActivityState
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var activityState by remember {
mutableStateOf(ActivityState.LOADING as ActivityState)
}
fullyDrawnReporter.addOnReportDrawnListener {
activityState = ActivityState.LOADED
}
ReportFullyDrawnTheme {
when(activityState) {
is ActivityState.LOADING -> {
// Display the loading UI.
}
is ActivityState.LOADED -> {
// Display the full UI.
}
}
}
SideEffect {
fullyDrawnReporter.addReporter()
lifecycleScope.launch(Dispatchers.IO) {
// Perform the background operation.
fullyDrawnReporter.removeReporter()
}
fullyDrawnReporter.addReporter()
lifecycleScope.launch(Dispatchers.IO) {
// Perform the background operation.
fullyDrawnReporter.removeReporter()
}
}
}
}
}
Identyfikowanie wąskich gardeł
Aby znaleźć wąskie gardła, możesz użyć CPU Profilera w Android Studio. Więcej informacji znajdziesz w artykule Sprawdzanie aktywności procesora za pomocą CPU Profilera.
Możesz też uzyskać wgląd w potencjalne wąskie gardła dzięki śledzeniu wbudowanemu w onCreate metody aplikacji i aktywności. Więcej informacji o śledzeniu wbudowanym znajdziesz w dokumentacji funkcji Trace i w omówieniu śledzenia systemu.
Rozwiązywanie typowych problemów
W tej sekcji omówimy kilka problemów, które często wpływają na wydajność uruchamiania aplikacji. Problemy te dotyczą głównie inicjowania obiektów aplikacji i aktywności oraz wczytywania ekranów.
Intensywna inicjalizacja aplikacji
Wydajność uruchamiania może się pogorszyć, jeśli kod zastępuje obiekt Application i wykonuje złożone operacje lub logikę podczas inicjowania tego obiektu. Aplikacja może tracić czas podczas uruchamiania, jeśli podklasy Application wykonują inicjalizacje, które nie muszą być jeszcze przeprowadzane.
Niektóre inicjalizacje mogą być całkowicie niepotrzebne, np. inicjalizacja informacji o stanie głównej aktywności, gdy aplikacja jest uruchamiana w odpowiedzi na intencję. W przypadku intencji aplikacja używa tylko podzbioru wcześniej zainicjowanych danych o stanie.
Inne problemy podczas inicjowania aplikacji to m.in. zdarzenia odśmiecania pamięci, które mają duży wpływ na wydajność lub występują w dużej liczbie, oraz operacje wejścia/wyjścia na dysku wykonywane równolegle z inicjowaniem, co dodatkowo blokuje proces inicjowania. Odśmiecanie pamięci jest szczególnie ważne w przypadku środowiska wykonawczego Dalvik. Środowisko wykonawcze Androida (ART) wykonuje odśmiecanie pamięci równolegle, co minimalizuje wpływ tej operacji.
Zdiagnozuj problem
Aby zdiagnozować problem, możesz użyć śledzenia metod lub śledzenia wbudowanego.
Śledzenie metod
Uruchomienie CPU Profilera ujawnia, że metoda callApplicationOnCreate ostatecznie wywołuje metodę com.example.customApplication.onCreate. Jeśli narzędzie pokazuje, że wykonanie tych metod zajmuje dużo czasu, sprawdź, jakie działania są w nich wykonywane.
Śledzenie w tekście
Używaj śledzenia wbudowanego, aby zbadać prawdopodobne przyczyny problemu, w tym:
- Początkowa funkcja
onCreateaplikacji. - Wszystkie globalne obiekty singleton, które inicjuje aplikacja.
- wszelkie operacje we/wy na dyskach, deserializację lub pętle, które mogą występować w miejscu wąskiego gardła.
Rozwiązania problemu
Niezależnie od tego, czy problem dotyczy niepotrzebnych inicjalizacji, czy operacji wejścia/wyjścia na dysku, rozwiązaniem jest leniwa inicjalizacja. Innymi słowy, inicjuj tylko obiekty, które są od razu potrzebne. Zamiast tworzyć globalne obiekty statyczne, używaj wzorca singleton, w którym aplikacja inicjuje obiekty tylko wtedy, gdy są one potrzebne po raz pierwszy.
Rozważ też użycie platformy wstrzykiwania zależności, takiej jak Hilt, która tworzy obiekty i zależności, gdy są one wstrzykiwane po raz pierwszy.
Jeśli Twoja aplikacja używa dostawców treści do inicjowania komponentów aplikacji podczas uruchamiania, rozważ użycie biblioteki uruchamiania aplikacji.
Inicjowanie intensywnej aktywności
Tworzenie aktywności często wiąże się z dużymi kosztami. Często istnieją możliwości optymalizacji tej pracy w celu poprawy wydajności. Do takich częstych problemów należą:
- Inicjowanie dużego lub złożonego interfejsu.
- Intensywna inicjalizacja w funkcjach kompozycyjnych.
- Blokowanie rysowania ekranu na dysku lub operacji wejścia/wyjścia sieciowego.
- Wczytywanie i dekodowanie bitmap.
- Rasteryzacja obiektów
VectorDrawable. - Inicjowanie innych podsystemów aktywności hosta aplikacji.
Zdiagnozuj problem
W tym przypadku przydatne mogą być zarówno śledzenie metod, jak i śledzenie wbudowane.
Śledzenie metod
Podczas korzystania z CPU Profilera zwróć uwagę na konstruktory podklas Application i metody com.example.customApplication.onCreate w aplikacji.
Jeśli narzędzie pokazuje, że wykonanie tych metod zajmuje dużo czasu, sprawdź, jakie działania są w nich wykonywane.
Śledzenie w tekście
Używaj śledzenia wbudowanego, aby zbadać prawdopodobne przyczyny problemu, w tym:
- początkowa funkcja
onCreateaplikacji; - wszelkie globalne obiekty singleton, które inicjuje;
- wszelkie operacje we/wy na dyskach, deserializację lub pętle, które mogą występować w miejscu wąskiego gardła.
Rozwiązania problemu
Może wystąpić wiele potencjalnych wąskich gardeł, ale 2 częste problemy i ich rozwiązania to:
- Im większa jest hierarchia interfejsu, tym więcej czasu zajmuje inicjowanie aplikacji.
Aby rozwiązać ten problem, wykonaj te czynności:
- Spłaszcz hierarchię interfejsu, zmniejszając liczbę zbędnych lub zagnieżdżonych funkcji kompozycyjnych.
- Unikaj niepotrzebnego komponowania i ponownego komponowania podczas uruchamiania.
- Opóźnij tworzenie niekrytycznych elementów interfejsu.
- Inicjowanie wszystkich zasobów w wątku głównym może również spowolnić uruchamianie. Aby rozwiązać ten problem:
- Przenieś całą inicjalizację zasobów, aby aplikacja mogła wykonywać ją w sposób odroczony w innym wątku.
- Najpierw wczytaj i wyświetl interfejs z danymi zastępczymi, a potem zaktualizuj właściwości wizualne, które zależą od bitmap i innych zasobów.
Więcej informacji o ponownym komponowaniu znajdziesz w sekcji Ponowne komponowanie oraz w artykule Uzyskiwanie liczby ponownych kompozycji.
Niestandardowe ekrany powitalne
Jeśli w Androidzie 11 (API na poziomie 30) lub starszym wdrożono niestandardowy ekran powitalny za pomocą jednej z tych metod, podczas uruchamiania może być widoczny dodatkowy czas:
- Używanie atrybutu motywu
windowDisablePreview, aby wyłączyć początkowy pusty ekran rysowany przez system podczas uruchamiania. - Użyj specjalnego elementu
Activity.
Od Androida 12 wymagana jest migracja do interfejsu SplashScreen API.
Ten interfejs API umożliwia szybsze uruchamianie i dostosowywanie ekranu powitalnego w następujący sposób:
- Ustaw motyw, aby zmienić wygląd ekranu powitalnego.
- Określ, jak długo ma być wyświetlany ekran powitalny, za pomocą parametru
windowSplashScreenAnimationDuration. - Dostosuj animację ekranu powitalnego i płynnie obsługuj animację zamykania ekranu powitalnego.
Biblioteka zgodności przenosi też SplashScreeninterfejs API do starszych wersji, aby zapewnić zgodność wsteczną i spójny wygląd ekranu powitalnego we wszystkich wersjach Androida.
Szczegółowe informacje znajdziesz w przewodniku po migracji ekranu powitalnego.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Powolne renderowanie
- Zbieranie danych testu porównawczego
- Tworzenie profili podstawowych{:#creating-profile-rules}