Cold Start — to pełny cykl uruchamiania aplikacji Androida rozpoczynający się od stanu zerowego, gdy proces aplikacji nie istnieje w pamięci, a Activity nie zostało utworzone. System tworzy nowy proces, ładuje klasy, inicjalizuje Application, tworzy Activity i wykonuje pierwsze renderowanie. Według danych Google, 2024, zimny start na urządzeniach średniej klasy może trwać od 1 do 5 sekund, a każde 100 ms opóźnienia zmniejsza prawdopodobieństwo utrzymania użytkownika o 3%.
Najważniejsze
Cold Start (zimny start) — to scenariusz, w którym aplikacja Android uruchamia się od stanu początkowego: system operacyjny tworzy nowy proces (fork z Zygote), przydziela pamięć, ładuje kod DEX do ART, inicjalizuje klasy i tworzy instancję Application, a następnie pierwsze Activity. Przed uruchomieniem aplikacji nie ma w pamięci urządzenia żadnych danych o niej, poza buforowanymi obrazami klas, jeśli używany jest Background Dexopt.
Zimny start występuje w trzech przypadkach: przy pierwszym uruchomieniu po instalacji aplikacji, przy uruchomieniu po ponownym starcie urządzenia oraz przy uruchomieniu po tym, jak system zwolnił proces z powodu braku pamięci. Na urządzeniach z 2–4 GB RAM system dość agresywnie zwalnia procesy działające w tle, dlatego Cold Start może występować przy każdym powrocie do aplikacji po kilku godzinach bezczynności. W Android 12+ system może zachować zamrożony proces (freeze / cached), ale przy aktywnej oszczędzaniu pamięci (OOM-killer) proces zostanie zniszczony.
Według Google (Find My Device report, 2023), 65% użytkowników zamyka aplikację, jeśli nie otworzy się w ciągu 3 sekund. W przypadku sieci społecznościowych i komunikatorów, do których użytkownik wraca kilkadziesiąt razy dziennie, Cold Start bezpośrednio wpływa na retention. W Google Play Console metryka Cold Start znajduje się w sekcji Android Vitals i jest wyświetlana jako jeden ze wskaźników ANR i wydajności. Aplikacja przekraczająca próg „złego” Cold Start (ponad 5 sekund na 25% urządzeń) otrzymuje ostrzeżenie w konsoli i może zostać obniżona w wyszukiwarce.
Android rozróżnia trzy typy uruchamiania aplikacji, z których każdy ma inną długość, wpływ na UX i podejście do optymalizacji. Zrozumienie różnicy jest niezbędne do wyboru właściwej strategii profilowania.
| Typ uruchomienia | Stan procesu | Application.onCreate | Typowy czas |
|---|---|---|---|
| Cold | Brak procesu | Wykonuje się | 1–5 sekund |
| Warm | Proces istnieje, brak Activity | Nie wykonuje się | 200–600 ms |
| Hot | Proces + Activity w pamięci | Nie wykonuje się | < 200 ms |
Warm Start występuje, gdy proces aplikacji już istnieje w tle, ale Activity zostało zniszczone (np. użytkownik wrócił po dłuższej przerwie, a system zwolnił pamięć Activity). Hot Start — gdy użytkownik minimalizuje aplikację i natychmiast otwiera ją ponownie: Activity jest wstrzymane, a przywrócenie zajmuje minimalną ilość czasu. Dla użytkownika Cold Start to najbardziej zauważalny typ uruchomienia i to właśnie jego optymalizacja daje największy przyrost UX.
Cold Start może stać się Warm Start po tym, jak aplikacja została uruchomiona przynajmniej raz — ART buforuje skompilowane obrazy klas (Image in Boot Profile), a ponowne ładowanie DEX odbywa się szybciej. Dlatego drugie uruchomienie po pierwszym Cold Starcie jest zwykle o 20–40% szybsze. Jeśli aplikacja używa Baseline Profiles, profile są ładowane przy pierwszym uruchomieniu, a drugi start może być jeszcze szybszy: Google Play, który opublikował Baseline Profiles, przyspieszył Cold Start o 30% na urządzeniach z Android 12+.
Cold Start składa się ze ściśle określonych faz, z których każda może być mierzona i optymalizowana niezależnie. Znajomość faz pomaga określić, na którym etapie aplikacja traci czas. Google wyróżnia cztery główne fazy: utworzenie procesu, inicjalizacja Application, utworzenie Activity i pierwsza klatka.
System Android (ActivityManagerService) tworzy nowy proces przez fork z procesu Zygote. Zygote to wstępnie załadowany proces z wspólnymi klasami Androida. Fork wykonuje się w 30–80 ms — to czas, na który aplikacja nie ma wpływu. Po fork uruchamiany jest ActivityThread — instancja głównej pętli aplikacji. Na tym etapie następuje również ładowanie klas przez ClassLoader, a ART zaczyna interpretować pierwszy bajt-kod. Jeśli aplikacja używa wielu statycznych inicjalizatorów, ta faza może się przeciągnąć.
Zaraz po uruchomieniu ActivityThread wywoływane jest Application.onCreate. Tu programista najczęściej popełnia błąd, inicjalizując wszystko od razu: Crashlytics, Firebase, klienty sieciowe, bazy danych, komponenty Dagger, kontenery DI. Każda taka inicjalizacja to czas zablokowany na głównym wątku. Jeśli Application.onCreate wykonuje się 500 ms, przez te pół sekundy użytkownik widzi biały (lub czarny) ekran. Optymalna długość tej fazy to mniej niż 200 ms na średnim urządzeniu.
Po inicjalizacji Application tworzona jest instancja Activity (MainActivity lub Launcher Activity). Wywoływane jest Activity.onCreate, gdzie następuje setContentView, inicjalizacja fragmentów, konfiguracja ViewModel, subskrypcja LiveData/Flow. Jeśli onCreate wykonuje ładowanie danych (SharedPreferences, SQLite, API) synchronicznie na głównym wątku, faza się wydłuża. Celem jest zmieścić onCreate w 200–400 ms na średnim urządzeniu.
Po zakończeniu onCreate rozpoczyna się pierwsze renderowanie: miara, layout, draw. Ten moment nazywa się TTFD (Time To First Draw). Jeśli aplikacja używa ekranu powitalnego (przez SplashScreen API na Android 12+ lub przez theme), renderowanie może nastąpić szybciej, ale użytkownik i tak będzie czekać, aż ekran powitalny zniknie. Idealny TTFD dla Cold Start to mniej niż 1.5 sekundy.
Pomiar Cold Start wymaga specjalnych narzędzi, ponieważ zwykłe logowanie (Log.d) rozpoczyna działanie dopiero po utworzeniu Application, a czas fork i ładowania klas pozostaje poza zasięgiem. Google zaleca trzy metody: polecenia ADB, Android Vitals i niestandardowe makra perf.
Najprostszy i powtarzalny sposób to polecenie adb shell am start -S -W. Flaga -S wymusza zatrzymanie aplikacji przed uruchomieniem (gwarantuje Cold Start). Polecenie wyświetla trzy metryki: ThisTime (czas uruchomienia Activity), TotalTime (łączny czas z uwzględnieniem uruchomienia procesu) i WaitTime (czas z uwzględnieniem wszystkich opóźnień Activity Manager). Dla czystości pomiaru wykonaj 5–7 pomiarów i weź medianę — pojedyncze pomiary są podatne na szumy (CPU throttling, obciążenie tła).
# Wymuszony Cold Start z pomiarem
$ adb shell am start -S -W \
com.example.app/.MainActivity
# Wynik polecenia:
# ThisTime: 1842 ms
# TotalTime: 1842 ms
# WaitTime: 1855 ms
Google Play Console zbiera anonimowe metryki ze wszystkich urządzeń, na których zainstalowana jest aplikacja. W sekcji Android Vitals → Launch time wyświetlany jest medianowy rozkład Cold Start według modelu urządzenia i wersji Androida. To jedyny sposób, aby zobaczyć rzeczywiste wartości na urządzeniach użytkowników, a nie na testowych smartfonach. Jeśli na Redmi 9A (2 GB RAM) Cold Start przekracza 5 sekund, a na Pixel 8 — 1.2 sekundy, problem leży w ilości pamięci i liczbie klas. Google pokazuje również odczuwalne przez użytkownika opóźnienie (user-perceptible delay) na podstawie 25 percentyla.
Google Jetpack Macrobenchmark (biblioteka androidx.benchmark) umożliwia pisanie instrumentowanych testów uruchamiania aplikacji. Test instaluje aplikację, uruchamia ją w stanie zimnym i mierzy czas do pierwszej klatki. Macrobenchmark automatycznie wykonuje 20 przebiegów, odrzuca odstające wartości i pokazuje stabilne percentyle. Dla CI/CD można porównać baseline i bieżące uruchomienie — jeśli czas się zwiększył, potok CI może zawieść.
Optymalizacja Cold Start to systemowa praca dotykająca kilku poziomów aplikacji: kodu, zasobów, konfiguracji kompilacji i architektury inicjalizacji. Google zaleca zaczynać od najdroższego — Application.onCreate — i przechodzić do drobiazgów.
Przenieś całą inicjalizację, która nie jest wymagana na starcie, z Application.onCreate do pierwszego punktu użycia. Firebase, Crashlytics, analytics SDK, push-notifications, komponenty DI — wszystko to można zainicjalizować po renderowaniu pierwszego ekranu. Użyj Lazy (by lazy) w Kotlin lub inicjalizacji ContentProvider z jawnym wywołaniem initialize(context). Według Google (Android Performance, 2023), leniwa inicjalizacja skraca Cold Start o 40–60% dla aplikacji używających 5+ SDK.
Baseline Profiles — to kompilacja AOT krytycznych klas i metod używanych na starcie aplikacji. Bez Baseline Profiles ART interpretuje kod DEX lub kompiluje go JIT-em, co zajmuje czas. Z profilami ART kompiluje wskazane metody do kodu natywnego (AOT) podczas instalacji aplikacji. Google twierdzi, że Baseline Profiles dają przyspieszenie Cold Start o 15–40% na Android 9+ i do 60% z optymalizacjami ART w Android 12+. Do tworzenia profili użyj wtyczki androidx.benchmark:benchmark-baseline-profile-gradle-plugin.
Biblioteka androidx.startup umożliwia uporządkowanie inicjalizacji komponentów i wykonanie jej w jednym ContentProvider. Zamiast wielu ContentProviderów z różnych bibliotek (każdy dodaje 1–2 ms do zimnego startu) App Startup łączy je w graf zależności i inicjalizuje ściśle w razie potrzeby. Na starcie wykonywane są tylko komponenty z @Initializer, oznaczone jako niezbędne dla pierwszego ekranu. Dla pozostałych ustawiana jest flaga needEarlyInit = false — uruchamiają się po pierwszym renderowaniu.
// App Startup Initializer — inicjalizacja po starcie
class AnalyticsInitializer : Initializer<Unit> {
override fun create(context: Context) {
Analytics.init(context)
}
override fun dependencies() = listOf<Class<out Initializer<*>>>()
}
// W AndroidManifest.xml oznaczamy jako opcjonalny
// <meta-data android:name="AnalyticsInitializer"
// android:value="false" />
Rozmiar pliku DEX bezpośrednio wpływa na czas jego ładowania przez ART. Użyj R8/ProGuard do zaciemniania i usuwania martwego kodu (MinifyEnabled = true). Włącz android:extractNativeLibs="false" w manifeście, aby APK nie rozpakowywał plików .so podczas instalacji. Dla projektów z 10 Reference Tracking dodawaj startup-priority tylko dla pierwszego ekranu. Każda dodatkowa metoda w DEX dodaje 0.5–2 ms do ładowania, a dla aplikacji z 50k+ metod (multidex z primary dex) — do 300 ms.
Android Vitals w Google Play Console (Launch time section) zbiera dane ze wszystkich urządzeń, na których zainstalowana jest aplikacja, pod warunkiem zgody użytkownika na anonimową diagnostykę. Metryki dzielą się na trzy kategorie: „good” (dobry), „moderate” (średni), „bad” (zły), w zależności od czasu Cold Start.
Google określa „bad” Cold Start jako czas przekraczający 5 sekund na dowolnym urządzeniu. Jednak w praktyce dla flagowych urządzeń (Snapdragon 8 Gen) za dobry uważa się czas poniżej 1.5 sekundy, dla średniej klasy — poniżej 2.5 sekundy, dla budżetowych — poniżej 4 sekund. Android Vitals pokazuje medianę dla każdego device model, co pozwala zrozumieć, na których urządzeniach aplikacja uruchamia się wolno. Jeśli Cold Start jest zły na urządzeniach Samsung A-series lub Xiaomi Redmi, przyczyną najczęściej jest słaba pamięć flash i mała ilość RAM (przyspieszenie przez Baseline Profiles daje największy efekt właśnie na takich urządzeniach).
Oprócz wyświetlania w konsoli, metryka Cold Start wpływa na ocenę jakości aplikacji w Google Play Search. Aplikacje z wysokim odsetkiem „złych” uruchomień otrzymują etykietę „Performance warning” na stronie instalacji, co obniża konwersję. Według Google (Android Performance Playbook, 2024), aplikacje, które rozwiązały problemy z Cold Start, średnio zwiększają konwersję instalacji o 5% i poprawiają wskaźnik retention (D1) o 3–7%.
Do bardziej szczegółowego monitorowania użyj Firebase Performance Monitoring. Śledzi on Cold Start na poziomie sesji, dzieli według wersji aplikacji i wersji Androida. W przeciwieństwie do Android Vitals, Firebase pokazuje diagram trace czasu spędzonego w poszczególnych fazach. Na przykład można zobaczyć, że w wersji 3.2.0 Application.onCreate zajmował 800 ms (z powodu nowej biblioteki push notifications), a w wersji 3.2.1 — 200 ms (po naprawie).
Poniżej znajdują się dwa praktyczne przykłady, które bezpośrednio przyspieszają Cold Start: przeniesienie inicjalizacji SDK po starcie i użycie SplashScreen API.
Typowy błąd — inicjalizacja wszystkich SDK w Application.onCreate. Poniżej pokazano, jak przenieść niekrytyczną inicjalizację do korutyny, która uruchamia się po renderowaniu pierwszej klatki. Ważne: Firebase, Crashlytics i Crash Reporting SDK muszą być zainicjalizowane na starcie — nie można ich odkładać, ponieważ łapią błędy podczas inicjalizacji innych komponentów. Dla pozostałych użyj lifecycleScope przy pierwszym Activity.
// ❌ Źle — cała inicjalizacja w Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this) // krytyczne
Analytics.init(this) // można później
Database.init(this) // można później
ImageLoader.init(this) // można później
}
}
// ✅ Dobrze — Firebase na starcie, reszta po inflate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this)
}
}
// W MainActivity po pierwszej klatce:
lifecycleScope.launchWhenResumed {
initializeNonCriticalSdks()
}
Na Android 12+ użyj oficjalnego SplashScreen API, które pokazuje systemowy splash (ikona aplikacji na ciemnym/jasnym tle) natychmiast po uruchomieniu procesu. To ukrywa czas inicjalizacji przed użytkownikiem — widzi on splash, a nie biały ekran. Dla starszych urządzeń użyj theme-based splash (Theme.SplashScreen w stylach). Ważne: splash nie powinien trwać dłużej niż 300 ms — jeśli w tym czasie aplikacja nie jest gotowa, narysuj „stały” szkielet (shimmer) i pokazuj postęp ładowania.
// SplashScreen API — Android 12+
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition {
isReady.value == false
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
// Theme-based splash (Android 5-11)
// W themes.xml:
// <style name="Theme.App.Starting" parent="Theme.SplashScreen">
// <item name="windowSplashScreenBackground">@color/white</item>
// <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
// </style>
Często zadawane pytania
Emulator używa wydajnego komputera-host i emuluje procesor z przyspieszeniem sprzętowym (HAXM / WHPX). Fizyczne urządzenia, zwłaszcza budżetowe (pamięć eMMC zamiast UFS), mają znacznie wolniejsze I/O. Zaleca się mierzyć Cold Start na fizycznym urządzeniu średniej klasy, aby uzyskać realistyczne dane.
Według zaleceń Google, medianowy Cold Start powinien być mniejszy niż 2 sekund na urządzeniach średniej klasy. Dla flagowców — mniej niż 1.5 sekundy. Dla urządzeń budżetowych (2 GB RAM) dopuszczalne jest do 4 sekund, ale zaleca się optymalizację do 3 sekund. Wartości powyżej 5 sekund są uważane za krytyczne.
Pośrednio — tak. Jeśli w manifeście wskazana jest ikona wektorowa (AdaptiveIcon), musi być skompilowana do drawable podczas uruchamiania. Jeśli ikona zawiera złożone ścieżki (pathData z dziesiątkami krzywych), kompilacja zajmuje 10–30 ms. Użyj VectorDrawable z zoptymalizowanym pathData (przez SVGOMG lub Android Studio Vector Asset).
Tak, jeśli Feature Module (Android App Bundle) ładowany jest na żądanie (on-demand), jego Cold Start liczy się od momentu kliknięcia na funkcję do pierwszej klatki. Moduły on-demand są ładowane przez Play Core Library, a ich instalacja dodaje 500–3000 ms do czasu uruchomienia. Optymalizuj kod funkcji tak samo, jak główny moduł.
Aplikacje z ponad 64k metodami wymagają Multidex. Oznacza to, że ART musi załadować kilka plików DEX, co zwiększa czas Cold Start o 200–800 ms w zależności od liczby classes.dex. Użyj minSdk 21+ (ART z obsługą natywnego multidex) i skonfiguruj primary dex przez --main-dex-list, aby krytyczne klasy znajdowały się w pierwszym pliku DEX.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również