Użytkownicy oczekują, że aplikacje będą się szybko wczytywać i będą reagować na ich działania. Aplikacja, która uruchamia się powoli, nie spełnia tych oczekiwań i może rozczarować użytkowników. Takie negatywne wrażenia mogą spowodować, że użytkownik oceni Twoją aplikację nisko w Sklepie Play lub nawet całkowicie z niej zrezygnuje.
Na tej stronie znajdziesz informacje, które pomogą Ci zoptymalizować czas uruchamiania aplikacji, w tym omówienie wewnętrznych procesów uruchamiania, sposoby profilowania wydajności podczas uruchamiania oraz typowe problemy z czasem uruchamiania wraz ze wskazówkami, jak je rozwiązać.
Informacje o różnych stanach uruchamiania aplikacji
Uruchomienie aplikacji może nastąpić w jednym z 3 stanów: uruchomienie całkowicie z pamięci, uruchomienie częściowo z pamięci lub uruchomienie z pamięci. Każdy stan wpływa na to, jak długo aplikacja będzie niewidoczna dla użytkownika. W przypadku uruchamiania „na zimno” aplikacja uruchamia się od zera. W pozostałych stanach system musi przenieść działającą aplikację z tła na pierwszy plan.
Zalecamy, aby zawsze optymalizować aplikację pod kątem uruchamiania na zimno. Może to też poprawić wydajność ciepłych i gorących uruchomień.
Aby zoptymalizować aplikację pod kątem szybkiego uruchamiania, warto wiedzieć, co dzieje się na poziomie systemu i aplikacji oraz jak te poziomy współdziałają ze sobą w każdym z tych stanów.
Dwa ważne wskaźniki do określania czasu uruchamiania aplikacji to czas do wyświetlenia pierwszej klatki (TTID) i czas do wyświetlenia całej klatki (TTFD). TTID to czas potrzebny na wyświetlenie pierwszej klatki, a TTFD to czas potrzebny na to, aby aplikacja stała się w pełni interaktywna. Oba są równie ważne, ponieważ TTID informuje użytkownika, że aplikacja się wczytuje, a TTFD to moment, w którym aplikacja jest już gotowa do użycia. Jeśli któryś z tych procesów trwa zbyt długo, użytkownik może zamknąć aplikację, zanim się w pełni załaduje.
Uruchomienie „na zimno”
Uruchomienie „na zimno” oznacza, że aplikacja jest uruchamiana od zera. Oznacza to, że do momentu uruchomienia proces systemowy tworzy proces aplikacji. Uruchomienie na zimno ma miejsce, gdy aplikacja jest uruchamiana po raz pierwszy od uruchomienia urządzenia lub od momentu, gdy system zamknął aplikację.
Ten typ uruchomienia stanowi największe wyzwanie, jeśli chodzi o minimalizowanie czasu uruchamiania, ponieważ system i aplikacja mają więcej pracy niż w przypadku innych stanów uruchamiania.
Na początku uruchomienia „na zimno” system wykonuje te 3 zadania:
- Wczytaj i uruchom aplikację.
- wyświetlać puste okno początkowe aplikacji bezpośrednio po jej uruchomieniu;
- Utwórz proces aplikacji.
Gdy tylko system utworzy proces aplikacji, jest on odpowiedzialny za kolejne etapy:
- Utwórz obiekt aplikacji.
- Uruchom wątek główny.
- Utwórz aktywność główną.
- Zainicjuj interfejs.
- wyświetlić interfejs na ekranie,
Gdy proces aplikacji zakończy pierwsze rysowanie, proces systemowy zamienia 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 rysowania aplikacji po raz pierwszy. W tym momencie proces systemowy zamienia okno początkowe na aplikację, umożliwiając użytkownikowi interakcję z nią.
Jeśli zastąpisz Application.onCreate we własnej aplikacji, system wywoła metodę onCreate w obiekcie aplikacji. Następnie aplikacja tworzy wątek główny, nazywany też wątkiem UI, i zleca mu utworzenie aktywności hosta aplikacji.
Od tego momentu procesy na poziomie systemu i aplikacji przebiegają zgodnie z etapami cyklu życia aplikacji.
Tworzenie aktywności
Gdy proces aplikacji utworzy aktywność, 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 narzut metody onCreate często wynika z początkowej kompozycji. Dzieje się tak, gdy aplikacja wywołuje funkcję setContent i wywołuje funkcje kompozycyjne najwyższego poziomu. Szczegółowa lub złożona hierarchia interfejsu albo komponenty, które wykonują skomplikowane obliczenia w głównym wątku, mogą wydłużyć czas tworzenia kompozycji.
Uruchamianie częściowo z pamięci
Uruchomienie częściowo z pamięci obejmuje podzbiór operacji, które mają miejsce podczas uruchomienia bez pamięci podręcznej. Jednocześnie wymaga więcej zasobów niż uruchomienie z pamięci. Istnieje wiele potencjalnych stanów, które można uznać za ciepłe uruchomienie, na przykład:
Użytkownik wychodzi z aplikacji, ale potem uruchamia ją ponownie. Proces może być nadal uruchomiony, ale aplikacja musi odtworzyć aktywność od podstaw, wywołując metodę
onCreate.System usuwa aplikację z pamięci, a użytkownik uruchamia ją ponownie. Proces i aktywność muszą zostać uruchomione ponownie, ale zadanie może w pewnym stopniu skorzystać z zapisanego pakietu stanu instancji przekazanego do metody
onCreate.
Uruchomienie z pamięci
Uruchomienie aplikacji z pamięci wymaga mniejszego nakładu pracy niż uruchomienie „na zimno”. W przypadku gorącego startu 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, inicjowania interfejsu i renderowania.
Jeśli jednak część pamięci zostanie wyczyszczona w odpowiedzi na zdarzenia przycinania pamięci, takie jak onTrimMemory, te obiekty trzeba będzie odtworzyć w odpowiedzi na zdarzenie szybkiego uruchomienia.
Uruchomienie z pamięci wygląda na ekranie tak samo jak uruchomienie bez pamięci. 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 aplikacji. Aby zidentyfikować całą fazę uruchamiania aplikacji w Perfetto, wykonaj te czynności:
W Perfetto znajdź wiersz z pochodnymi danymi o uruchamianiu aplikacji na Androida. Jeśli go nie widzisz, spróbuj przechwycić ślad za pomocą aplikacji do śledzenia systemu na urządzeniu.
Rysunek 2. Wycinek pochodnych danych o uruchamianiu aplikacji na Androida w Perfetto. Kliknij powiązany wycinek i naciśnij m, aby go wybrać. Wokół wycinka pojawiają się nawiasy, które wskazują, ile czasu zajęło jego wykonanie. 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 najedziesz kursorem na wiersz.
Przewiń do wiersza z daną aplikacją i kliknij pierwszą komórkę, aby rozwinąć wiersz.
Powiększ główny wątek, który zwykle znajduje się u góry, naciskając W (naciśnij S, A, D, aby odpowiednio pomniejszyć, przesunąć w lewo i przesunąć w prawo).
Rysunek 3. Pochodny wycinek danych dotyczących uruchamiania aplikacji na Androida obok głównego wątku aplikacji. Wycinek danych pochodnych ułatwia sprawdzenie, co dokładnie jest uwzględnione w uruchamianiu aplikacji, dzięki czemu możesz kontynuować debugowanie z większą szczegółowością.
Podczas analizowania aplikacji Jetpack Compose możesz użyć śledzenia kompozycji, aby zobaczyć szczegółowe informacje o wydajności interfejsu.
Zwróć szczególną uwagę na sekcje śladu w wątku głównym, które zaczynają się od Choreographer#doFrame. Szukaj wycinków takich jak Compose:recompose, Compose:layout i Compose:draw. Pokazują one, ile czasu aplikacja poświęca na tworzenie, mierzenie i umieszczanie oraz rysowanie określonych komponentów.
Używanie danych do sprawdzania i ulepszania uruchamiania aplikacji
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 na pokazanie, że aplikacja ma problem, i pomaga go zdiagnozować.
Korzyści z wykorzystania danych o uruchamianiu
Android używa danych czas do wyświetlenia pierwszej klatki (TTID) i czas do wyświetlenia całej klatki (TTFD), aby optymalizować uruchamianie aplikacji na zimno i na ciepło. Środowisko wykonawcze Androida (ART) wykorzystuje dane z tych danych, aby efektywnie wstępnie kompilować kod w celu optymalizacji przyszłych uruchomień.
Szybsze uruchamianie prowadzi do dłuższego korzystania z aplikacji przez użytkowników, co zmniejsza liczbę przypadków przedwczesnego wyjścia, ponownego uruchomienia instancji lub przejścia do innej aplikacji.
Czas do początkowego wyświetlania
Czas do początkowego wyświetlania (TTID) to czas potrzebny na wyświetlenie pierwszej klatki interfejsu aplikacji. Te dane mierzą czas potrzebny aplikacji na wygenerowanie pierwszej klatki, w tym inicjalizację procesu podczas uruchamiania na zimno, utworzenie aktywności podczas uruchamiania na zimno lub na ciepło oraz wyświetlenie pierwszej klatki. Utrzymywanie niskiego TTID aplikacji pomaga poprawić wrażenia użytkowników, ponieważ pozwala im szybko zobaczyć uruchomienie aplikacji. TTID jest raportowany automatycznie dla każdej aplikacji przez platformę Androida. Podczas optymalizacji pod kątem uruchamiania aplikacji zalecamy wdrożenie reportFullyDrawn, aby uzyskać informacje do momentu TTFD.
TTID jest mierzony jako wartość czasu, która reprezentuje łączny czas, który upłynął, obejmujący następującą sekwencję zdarzeń:
- Uruchamianie procesu.
- Inicjuję obiekty.
- Tworzenie i inicjowanie aktywności hosta.
- Inicjowanie interfejsu.
- Pierwsze rysowanie aplikacji.
Pobieranie identyfikatora TTID
Aby znaleźć TTID, wyszukaj w narzędziu wiersza poleceń Logcat wiersz wyjściowy zawierający wartość o nazwie Displayed. Ta wartość to TTID i wygląda podobnie do tego przykładu, w którym TTID wynosi 3 s 534 ms:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
Aby znaleźć TTID w Android Studio, wyłącz filtry w widoku Logcat w menu filtra, a następnie znajdź czas Displayed, jak pokazano na rysunku 4.
Wyłączenie filtrów jest konieczne, ponieważ ten log jest obsługiwany przez serwer systemowy, a nie przez samą aplikację.
Displayed w Logcat.Dane Displayed w danych wyjściowych Logcat nie muszą odzwierciedlać czasu potrzebnego na wczytanie i wyświetlenie wszystkich zasobów. Pomija zasoby, do których nie ma odwołań w początkowej kompozycji (takie jak asynchronicznie wczytywane dane lub obrazy) lub zasoby, które aplikacja tworzy w ramach inicjowania obiektów. Te zasoby są wykluczone, 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 łącznym czasem, jak w tym przykładzie:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)
W takim przypadku pomiar czasu pierwszego renderowania dotyczy tylko aktywności, która jest renderowana jako pierwsza, czyli zwykle aktywności hosta. Pomiar czasu total rozpoczyna się w momencie uruchomienia procesu aplikacji i może obejmować inną aktywność, która została uruchomiona 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 pojedynczego działania a łącznym czasem uruchamiania.
Zalecamy korzystanie z Logcat w Android Studio, ale jeśli nie używasz Android Studio, możesz też zmierzyć TTID, uruchamiając aplikację za pomocą polecenia menedżera aktywności powłoki adb. 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
Wskaźnik Displayed pojawia 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 pozwalają określić <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 on podawany jako czas potrzebny na wyświetlenie pierwszej klatki interfejsu aplikacji oraz treści, które są wczytywane asynchronicznie po wyświetleniu pierwszej klatki. Zwykle jest to główna treść wczytywana z sieci lub dysku, zgodnie z raportem aplikacji. Innymi słowy, TTFD obejmuje TTID oraz czas potrzebny na to, aby aplikacja była gotowa do użycia. Utrzymywanie niskiego TTFD aplikacji pomaga poprawić wrażenia użytkowników, umożliwiając im szybką interakcję z aplikacją.
System może określić TTID, gdy okno hosta renderuje początkową klatkę, ale nie może automatycznie określić TTFD. Aplikacje często ładują główną zawartość asynchronicznie, więc system nie wie, kiedy aplikacja jest w pełni gotowa do użycia. Aby określić TTFD, aplikacja musi zasygnalizować systemowi, kiedy osiągnie stan pełnego wyrenderowania.
Pobieranie TTFD
Aby znaleźć TTFD, zasygnalizuj stan pełnego wyrenderowania, wywołując metodę reportFullyDrawn klasy ComponentActivity. Metoda reportFullyDrawn informuje, kiedy aplikacja jest w pełni wyrenderowana i gotowa do użycia. TTFD to czas, który upływa od momentu, gdy system otrzyma intencję uruchomienia aplikacji, do wywołania metody reportFullyDrawn. Jeśli nie wywołasz metody reportFullyDrawn, wartość TTFD nie zostanie zarejestrowana.
Aby zmierzyć TTFD, wywołaj reportFullyDrawn po całkowitym wyrenderowaniu interfejsu i wszystkich danych. Nie wywołuj funkcji reportFullyDrawn, zanim pierwsze okno aktywności zostanie narysowane i wyświetlone zgodnie z pomiarem systemu, ponieważ wtedy system zgłosi czas zmierzony przez system. Innymi słowy, jeśli wywołasz funkcję reportFullyDrawn, zanim system wykryje TTID, system zgłosi TTID i TTFD jako tę samą wartość, którą będzie wartość TTID.
Gdy używasz reportFullyDrawn, Logcat wyświetla dane wyjściowe podobne do tego 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ą czas total, jak opisano w sekcji Czas do początkowego wyświetlenia.
Jeśli czas wyświetlania jest dłuższy, niż chcesz, możesz spróbować zidentyfikować wąskie gardła w procesie uruchamiania.
W podstawowych przypadkach, gdy wiesz, że stan pełnego narysowania został osiągnięty, możesz użyć reportFullyDrawn, aby go zasygnalizować. Jeśli jednak wątki w tle muszą zakończyć pracę, zanim zostanie osiągnięty stan pełnego narysowania, musisz opóźnić wywołanie 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 aplikacja korzysta z leniwego ładowania, a początkowy ekran nie zawiera wszystkich zasobów, np. gdy aplikacja pobiera obrazy z sieci, możesz opóźnić wywołanie funkcji reportFullyDrawn, dopóki aplikacja nie będzie gotowa do użycia. Dzięki temu możesz uwzględnić wypełnianie listy w czasie testu porównawczego.
Jeśli na przykład interfejs zawiera listę dynamiczną, taką jak LazyColumn lub LazyRow, może ona zostać wypełniona 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 lista nie jest uwzględniana w analizie porównawczej.
Aby uwzględnić populację listy w ramach czasu testu porównawczego, pobierz FullyDrawnReporter za pomocą fullyDrawnReporter i dodaj do niego reportera w kodzie aplikacji. Zwolnij reportera po zakończeniu wypełniania listy przez zadanie w tle.
FullyDrawnReporter nie wywołuje metody reportFullyDrawn, dopóki nie zostaną zwolnione wszystkie dodane raporty. Dodając reportera, ale nie udostępniając go, dopóki proces w tle nie zostanie zakończony, masz pewność, że dane o czasie uruchamiania będą zawierać czas wypełniania listy, bez zmiany działania aplikacji dla użytkownika. Funkcja reportFullyDrawn jest wywoływana dopiero po zakończeniu wszystkich zadań, niezależnie od kolejności.
Jeśli aplikacja używa Jetpack Compose, możesz użyć tych interfejsów API, aby wskazać stan pełnego wyrenderowania:
ReportDrawn: wskazuje, że funkcja kompozycyjna jest natychmiast gotowa do interakcji.ReportDrawnWhen: przyjmuje predykat, np.list.count > 0, aby wskazać, kiedy funkcja kompozycyjna jest gotowa do interakcji.ReportDrawnAfter: przyjmuje metodę zawieszania, która po zakończeniu wskazuje, że funkcja kompozycyjna jest gotowa do interakcji.
Poniższy przykład pokazuje, jak można uruchomić wiele zadań w tle jednocześnie, z których każde rejestruje własny raport:
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ć narzędzia CPU Profiler w Android Studio. Więcej informacji znajdziesz w artykule Sprawdzanie aktywności procesora za pomocą narzędzia CPU Profiler.
Możesz też uzyskać wgląd w potencjalne wąskie gardła dzięki śledzeniu wbudowanemu w metody onCreate aplikacji i aktywności. Aby dowiedzieć się więcej o śledzeniu wbudowanym, zapoznaj się z dokumentacją funkcji Trace i omówieniem śledzenia systemu.
Rozwiązywanie typowych problemów
W tej sekcji omówiono kilka problemów, które często wpływają na wydajność uruchamiania aplikacji. Te problemy dotyczą głównie inicjowania obiektów aplikacji i aktywności oraz wczytywania ekranów.
Zasobochłonna inicjalizacja aplikacji
Wydajność uruchamiania może się pogorszyć, gdy kod zastępuje obiekt Application i wykonuje skomplikowane zadania lub złożoną logikę podczas inicjowania tego obiektu. Aplikacja może marnować czas podczas uruchamiania, jeśli podklasy Application wykonują inicjalizacje, które nie są jeszcze potrzebne.
Niektóre inicjalizacje mogą być zupełnie 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. liczne lub znaczące zdarzenia związane z odzyskiwaniem pamięci albo operacje wejścia/wyjścia na dysku wykonywane jednocześnie z inicjowaniem, co dodatkowo blokuje proces inicjowania. Zwalnianie pamięci jest szczególnie ważne w przypadku środowiska wykonawczego Dalvik. Środowisko wykonawcze Androida (ART) wykonuje tę operację równolegle, co minimalizuje jej wpływ na działanie aplikacji.
Diagnozowanie problemu
Aby spróbować zdiagnozować problem, możesz użyć śledzenia metod lub śledzenia w trybie inline.
Śledzenie metod
Uruchomienie CPU Profilera pokazuje, że metoda callApplicationOnCreate ostatecznie wywołuje metodę com.example.customApplication.onCreate. Jeśli narzędzie pokazuje, że te metody są wykonywane przez długi czas, sprawdź, jakie działania są w nich realizowane.
Śledzenie w tekście
Użyj śledzenia wbudowanego, aby zbadać prawdopodobne przyczyny, w tym:
- Początkowa funkcja
onCreateaplikacji. - wszystkie globalne obiekty singleton, które inicjuje aplikacja;
- operacje we/wy na dysku, deserializację lub pętle, które mogą występować podczas wąskiego gardła;
Rozwiązania problemu
Niezależnie od tego, czy problem dotyczy niepotrzebnych inicjalizacji czy operacji we/wy dysku, rozwiązaniem jest leniwa inicjalizacja. Innymi słowy, inicjuj tylko te obiekty, które są potrzebne od razu. Zamiast tworzyć globalne obiekty statyczne, przejdź na wzorzec singleton, w którym aplikacja inicjuje obiekty tylko wtedy, gdy są 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ą wstrzykiwane po raz pierwszy.
Jeśli Twoja aplikacja używa mechanizmów dostarczania treści do inicjowania komponentów aplikacji podczas uruchamiania, rozważ użycie biblioteki App Startup.
Inicjowanie intensywnej aktywności
Tworzenie aktywności często wiąże się z dużą ilością pracy o wysokich kosztach ogólnych. Często można zoptymalizować te zadania, aby poprawić wydajność. Przykłady typowych problemów:
- inicjowanie dużego lub złożonego interfejsu;
- Ciężka inicjalizacja w funkcjach kompozycyjnych.
- blokowanie rysowania ekranu na dysku lub operacji we/wy sieci.
- Wczytywanie i dekodowanie bitmap.
- Rasteryzuję obiekty (
VectorDrawable). - inicjowanie innych podsystemów aktywności hosta aplikacji.
Diagnozowanie problemu
W tym przypadku przydatne mogą być zarówno śledzenie metod, jak i śledzenie wbudowane.
Śledzenie metod
Podczas korzystania z narzędzia CPU Profiler zwróć uwagę na konstruktory podklas Application i metody com.example.customApplication.onCreate aplikacji.
Jeśli narzędzie pokazuje, że wykonywanie tych metod zajmuje dużo czasu, sprawdź, jakie zadania są w nich realizowane.
Śledzenie w tekście
Użyj śledzenia wbudowanego, aby zbadać prawdopodobne przyczyny, w tym:
- Początkowa funkcja
onCreateaplikacji. - wszystkie inicjowane przez niego globalne obiekty singleton.
- operacje we/wy na dysku, deserializację lub pętle, które mogą występować podczas wąskiego gardła;
Rozwiązania problemu
Istnieje wiele potencjalnych wąskich gardeł, ale 2 typowe problemy i ich rozwiązania to:
- Im większa hierarchia interfejsu, tym więcej czasu zajmuje aplikacji jej inicjowanie.
Aby rozwiązać ten problem, wykonaj te czynności:
- Spłaszcz hierarchię interfejsu, zmniejszając liczbę nadmiarowych lub zagnieżdżonych funkcji kompozycyjnych.
- Unikaj niepotrzebnego tworzenia i ponownego tworzenia kompozycji podczas uruchamiania.
- Odłóż kompozycję niekrytycznego interfejsu.
- Inicjowanie wszystkich zasobów w głównym wątku może też spowolnić uruchamianie. Aby rozwiązać ten problem:
- Przenieś całą inicjalizację zasobów, aby aplikacja mogła ją wykonywać w innym wątku.
- Najpierw wczytaj i wyświetl interfejs z danymi zastępczymi, a później zaktualizuj właściwości wizualne, które zależą od bitmap i innych zasobów.
Więcej informacji o rekompozycji znajdziesz w sekcjach Rekompozycja i Uzyskiwanie liczby rekompozycji.
Niestandardowe ekrany powitalne
Jeśli wcześniej użyto jednej z tych metod do zaimplementowania niestandardowego ekranu powitalnego w Androidzie 11 (poziom interfejsu API 30) lub starszym, podczas uruchamiania może pojawić się dodatkowy czas:
- Użycie atrybutu motywu
windowDisablePreviewdo wyłączenia początkowego pustego ekranu wyświetlanego przez system podczas uruchamiania. - Używanie dedykowanego elementu
Activity.
Począwszy od Androida 12, wymagana jest migracja do interfejsu API SplashScreen.
Ten interfejs API umożliwia szybsze uruchamianie i pozwala dostosować ekran powitalny na te sposoby:
- Ustaw motyw, aby zmienić wygląd ekranu powitalnego.
- Określ, jak długo ma być wyświetlany ekran powitalny, za pomocą
windowSplashScreenAnimationDuration. - Dostosuj animację ekranu powitalnego i odpowiednio obsłuż animację zamykania ekranu powitalnego.
Ponadto biblioteka zgodności obsługuje interfejs API SplashScreen, 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
- Przechwytywanie danych makrotestu porównawczego
- Tworzenie profili podstawowych{:#creating-profile-rules}