Warm Start: istota, ciepły start i optymalizacja w Androidzie

Autor: IT Sectr Opublikowano: 2026-03-31 Czas czytania: 8 min

Warm Start — to scenariusz uruchomienia aplikacji Android, w którym proces aplikacji już istnieje w pamięci (na przykład po zminimalizowaniu), ale Activity została zniszczona przez system w celu oszczędności zasobów. Metoda Application.onCreate została już wykonana, klasy załadowane, ale UI jest tworzony od nowa. Według Google, 2024, Warm Start trwa od 200 do 800 ms i stanowi około 40% wszystkich uruchomień na urządzeniach z 4 GB RAM.

Najważniejsze

  • Warm Start — uruchomienie aplikacji z istniejącym procesem, ale bez Activity w pamięci
  • Różnica od Cold Start: Application.onCreate nie jest wykonywane, klasy już załadowane
  • Czas Warm Start wynosi 200–800 ms w porównaniu z 1–5 sekundami dla Cold Start
  • Scenariusze: powrót do aplikacji po kilku godzinach, zwolnienie Activity przez OOM-killer
  • Optymalizacja koncentruje się na zachowaniu stanu Activity i buforowaniu danych

Co to jest Warm Start

Warm Start (ciepłe uruchomienie) — to stan pomiędzy Cold Start a Hot Start: proces aplikacji istnieje w pamięci (czasami w tle pamięci podręcznej Linuksa), ale Activity nie jest aktywne i zostanie utworzone od nowa. System Android przy niedoborze pamięci operacyjnej może zwolnić Activity ze stosu, pozostawiając proces przy życiu. Gdy użytkownik wróci do aplikacji, uruchamiany jest Warm Start: tworzona jest nowa instancja Activity, wykonywane są metody cyklu życia onCreate → onStart → onResume, ale Application.onCreate i ładowanie klas są pomijane.

Przyczyny Warm Start

System Android podejmuje decyzję o zwolnieniu Activity na podstawie priorytetu procesu (importance rank). Activity w tle (poziom PROCESS_STATE_IMPORTANT_FOREGROUND lub PROCESS_STATE_TOP_SLEEPING) może zostać zniszczona po 5–30 minutach od zminimalizowania aplikacji, w zależności od dostępnego RAM. Na urządzeniach z 3 GB RAM Activity może zostać zwolniona już po 10 minutach, na urządzeniach z 8 GB — po kilku godzinach. Ważne: przy Warm Start onSaveInstanceState jest wywoływane przed zniszczeniem Activity, a programista może zapisać stan UI.

Odbiór przez użytkownika

Użytkownik nie widzi różnicy między Warm a Cold Start — po prostu klika na ikonę aplikacji i czeka. Jednak przy Warm Start biały ekran (blank window) może się pojawić, jeśli aplikacja nie skonfigurowała własnego motywu dla okna startowego. Google zaleca ustawienie niestandardowego motywu w manifeście (Theme.AppCompat.Light lub Theme.Material3.DayNight) dla startowego Activity, aby uniknąć migotania białego/czarnego ekranu przy Warm Start. Na Android 12+ SplashScreen API również ukrywa ten efekt.

Warm Start vs Cold Start vs Hot Start

Zrozumienie różnicy między trzema typami uruchamiania jest niezbędne do wyboru właściwej strategii profilowania i optymalizacji. Każdy typ ma swój czas trwania, swoje wąskie gardła i swoje narzędzia pomiarowe.

KryteriumCold StartWarm StartHot Start
ProcesTworzony od nowaIstnieje w pamięciIstnieje w pamięci
Application.onCreateWykonywaneNie wykonywaneNie wykonywane
ActivityTworzona od zeraTworzona od zeraPrzywracana ze stosu
Czas1–5 sekund200–800 ms< 200 ms
onCreate ActivityPełnyPełny (z przywróceniem)Pomijany

W praktyce Warm Start stanowi od 30% do 60% wszystkich uruchomień aplikacji, w zależności od nawyków użytkownika i ilości RAM urządzenia. Użytkownicy, którzy trzymają wiele aplikacji otwartych (multitasker), częściej spotykają się z Warm Start. Dla sieci społecznościowych i komunikatorów Warm Start to najczęstszy scenariusz, ponieważ aplikacja jest ciągle w tle. Dla aplikacji bankowych, przeciwnie, przeważa Cold Start (wymuszone czyszczenie procesu ze względów bezpieczeństwa).

Fazy ciepłego uruchamiania

Warm Start składa się z trzech faz, z których każda może być zmierzona i zoptymalizowana. W przeciwieństwie do Cold Start, nie ma tu fazy fork i ładowania klas, ale jest faza przywracania stanu (restore), która może być kosztowna.

Faza 1: Okno startowe (window background)

System sprawdza, czy aplikacja ma motyw dla okna startowego. Jeśli motyw nie jest ustawiony, wyświetlany jest biały (lub czarny, w zależności od systemu) ekran. Jeśli motyw jest ustawiony, wyświetlane jest tło z motywu. Ta faza trwa 10–30 ms, ale wizualnie jest odczuwalna, jeśli motyw nie pasuje do rzeczywistego UI aplikacji. Używaj Theme.Material3.DayNight z niestandardowym windowBackground, którego kolor pasuje do tła pierwszego ekranu — to tworzy efekt natychmiastowego ładowania.

Faza 2: Tworzenie Activity (restore)

System wywołuje onCreate z przekazaniem Bundle savedInstanceState, który został zapisany w onSaveInstanceState przed zniszczeniem Activity. Jeśli aplikacja prawidłowo zapisała stan (tekst pól, pozycję przewijania, dane ViewModel), przywrócenie następuje szybko. Jeśli nie — Activity zaczyna od czystej karty, a użytkownik widzi loader, dopóki dane się ładują. Kluczowy moment: obiekty ViewModel przetrwają Warm Start tylko wtedy, gdy proces nie został zniszczony — przy Warm Start ViewModel pozostaje w pamięci.

Faza 3: Pierwsza klatka (TTFD)

Po onCreate wykonuje się onStart → onResume, a system wywołuje pierwsze renderowanie. TTFD (Time To First Draw) dla Warm Start powinien być poniżej 300 ms na średnim urządzeniu. Jeśli pierwszy ekran zawiera złożony RecyclerView z ciężkimi View lub ładuje obrazy przez sieć, TTFD może przekroczyć próg. Używaj Placeholder i Shimmer do płynnego ładowania treści po pierwszej klatce.

Jak zmierzyć Warm Start

Pomiar Warm Start jest trudniejszy niż Cold Start, ponieważ trzeba symulować stan „proces żyje, Activity zniszczona”. Standardowa komenda ADB z flagą -S nie działa — zabija proces. Do Warm Start używaj innych podejść.

ADB shell am start bez -S

Najpierw uruchom aplikację przez adb shell monkey lub tap na ikonę, następnie zminimalizuj ją (adb shell input keyevent 3 keyevent HOME). Odczekaj 5–10 sekund, aby system mógł zwolnić Activity, i uruchom adb shell am start -W (bez -S). Komenda zwróci czas uruchomienia, który będzie krótszy niż Cold Start. Dla powtarzalności używaj skryptu: uruchom → odczekaj → home → odczekaj → uruchom.

bash
# Symulacja Warm Start przez ADB
$ adb shell am start -W \
    com.example.app/.MainActivity

# Wynik (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms

Macrobenchmark dla Warm Start

Biblioteka androidx.benchmark.macro obsługuje pomiar Warm Start. W tym celu w teście ustaw startupMode = StartupMode.WARM — biblioteka uruchomi aplikację, zminimalizuje ją, odczeka (configurable delay), a następnie zmierzy ponowne uruchomienie. Macrobenchmark wykonuje 10–20 prób i oblicza percentyle. W CI/CD można ustawić próg: jeśli P50 Warm Start przekracza 600 ms — test nie przechodzi. Pozwala to śledzić regresje przy każdym commicie.

Firebase Performance Monitoring

Firebase automatycznie rozróżnia Cold i Warm Start na podstawie czasu od poprzedniego zamknięcia aplikacji. Jeśli aplikacja była otwarta w ciągu ostatnich 30 minut, Firebase klasyfikuje uruchomienie jako Warm. W konsoli Firebase zobaczysz osobne wykresy dla każdego typu uruchomienia, co pozwala ocenić skuteczność optymalizacji. Na przykład po wdrożeniu zapisywania stanu w ViewModel można zaobserwować spadek Warm Start o 30%.

Jak optymalizować Warm Start

Optymalizacja Warm Start koncentruje się na dwóch kierunkach: przyspieszenie Activity.onCreate i prawidłowe przywracanie stanu. Ponieważ Application.onCreate i ładowanie klas są już wykonane, głównym wąskim gardłem jest kod UI pierwszego ekranu.

Asynchroniczne przywracanie stanu

Jeśli zapisany stan (savedInstanceState) zawiera dane, które trzeba zdeserializować (Bitmap, String, JSON), rób to w wątku tła. Zamiast bezpośredniego odczytu z Bundle w onCreate, uruchom korutynę i pokaż ekran shimmer. W praktyce deserializacja Bundle na średnim urządzeniu zajmuje 20–100 ms — wydaje się niewiele, ale dla Warm Start to 10–50% całego czasu. Używaj Saved State Module biblioteki Jetpack, która automatycznie zapisuje i przywraca stan ViewModel w Bundle lub bazie danych.

Optymalizacja setContentView

Rozwijanie układu XML (layout inflation) — jeden z najdroższych etapów Warm Start. Jeśli pierwszy ekran używa złożonego CoordinatorLayout z AppBar, CollapsingToolbar, NestedScrollView plus trzy RecyclerView, czas inflation może osiągnąć 300 ms. Rozwiązania: używaj ConstraintLayout dla płaskiej hierarchii, stosuj ViewStub dla niewidocznych na starcie sekcji (bottom sheet, dialog), włącz asynchroniczne rozwijanie dla ciężkich fragmentów przez AsyncLayoutInflater. W Jetpack Compose inflation nie jest potrzebny, ale kompilacja drzewa Compose na Warm Start może zajmować podobny czas.

Buforowanie danych

Przy Warm Start dane, które aplikacja ładowała w poprzedniej sesji, mogą być już w pamięci podręcznej: baza danych Room, SharedPreferences, pamięć podręczna in-memory w ViewModel. Jeśli twój pierwszy ekran pokazuje listę z serwera, sprawdzaj pamięć podręczną przy starcie i aktualizuj dane w tle. Używaj strategii cache-then-network: najpierw wyświetl dane z pamięci podręcznej (natychmiast), potem zaktualizuj z serwera (asynchronicznie). Skraca to postrzegany czas Warm Start do 100–200 ms.

kotlin
// ViewModel z buforowaniem dla Warm Start
class FeedViewModel : ViewModel() {
    private val cache = MutableStateFlow<List<Item>>(emptyList())

    init {
        // Najpierw pamięć podręczna, potem sieć
        viewModelScope.launch {
            cache.emit(db.getItems()) // Warm Start: dane już w bazie
            cache.emit(api.fetchItems()) // Aktualizacja w tle
        }
    }
}

Zachowanie stanu przy Warm Start

Prawidłowe zapisywanie stanu — to kluczowy czynnik odróżniający dobry Warm Start od złego. Użytkownik oczekuje powrotu do aplikacji i zobaczenia tego samego, co zostawił — łącznie z pozycją przewijania, tekstem w polach, wybranymi zakładkami.

onSaveInstanceState

System wywołuje onSaveInstanceState przy niszczeniu Activity, ale ZANIM proces może zostać zabity. W Bundle zapisywane są tylko proste dane (String, Int, Parcelable, Serializable). Do złożonych danych używaj SavedStateHandle w ViewModel — automatycznie zapisuje i przywraca pola przy Warm Start. W przeciwieństwie do onSaveInstanceState, SavedStateHandle działa nawet jeśli proces przetrwał Warm Start (ViewModel nie jest niszczony). Przykład: dla tekstu w EditText używaj SavedStateHandle.getLiveData(“text”) — tekst zapisze się i przywróci automatycznie.

ViewModel i Warm Start

Jeśli przy Warm Start proces nie został zabity, ViewModel pozostaje w pamięci i onCleared nie jest wywoływane. Oznacza to, że wszystkie dane załadowane w poprzedniej sesji są dostępne natychmiast. Jednak jeśli proces został zabity (urządzenie w głębokim uśpieniu ponad 30 minut), ViewModel jest niszczony i tworzony od nowa z SavedStateHandle. Dla poprawnego działania ViewModel przy Warm Start używaj SavedStateHandle z polami, które trzeba przywrócić w każdym scenariuszu. Różnica: ViewModel z @HiltViewModel obsługuje SavedStateHandle automatycznie.

MechanizmProces żyjeProces zabity
ViewModelDane w pamięciZniszczony, tworzony od nowa
SavedStateHandleDane w pamięciPrzywracane z Bundle
onSaveInstanceStateWywoływane przy zwolnieniu ActivityNie wywoływane
Room DBPamięć podręczna dostępnaPamięć podręczna dostępna (dysk)

Zapisywanie przewijania RecyclerView

Jeden z najczęstszych problemów Warm Start — utrata pozycji przewijania. Użytkownik przewijał listę do 50. elementu, zminimalizował aplikację, wrócił — i widzi początek listy. Rozwiązanie: zapisuj layoutManager.onSaveInstanceState (zapisuje pozycję i offset pierwszego widocznego elementu) i przywracaj go w onRestoreInstanceState. Można również zapisywać ostatnią widoczną pozycję w SharedPreferences z kluczem według daty/czasu, aby przy Warm Start szybko przywrócić pozycję.

kotlin
// Zapisywanie pozycji przewijania RecyclerView
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putParcelable(
        "rv_state", binding.recyclerView
            .layoutManager?.onSaveInstanceState()
    )
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    savedInstanceState?.getParcelable<Parcelable>("rv_state")
        ?.let { binding.recyclerView.layoutManager?.onRestoreInstanceState(it) }
}

Przykłady kodu dla Warm Start

Dwa praktyczne przykłady optymalizacji Warm Start: użycie SavedStateHandle w ViewModel i asynchroniczne przywracanie złożonych danych po starcie.

ViewModel z SavedStateHandle

SavedStateHandle automatycznie zapisuje pola w Bundle i przywraca je przy Warm Start. Pole profilu użytkownika (String, JSON) zostanie przywrócone bez zbędnych zapytań do serwera. Jeśli proces został zabity, SavedStateHandle załaduje ostatni zapisany stan z Bundle.

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val profile: StateFlow<Profile?>
        get() = savedStateHandle
            .getStateFlow("profile", null)

    fun loadProfile(id: String) {
        viewModelScope.launch {
            savedStateHandle["profile"] =
                api.getProfile(id)
        }
    }
}

// Warm Start: profil nie jest null, UI bez loadera
// Po załadowaniu: profil aktualizuje się w SavedStateHandle

AsyncLayoutInflater dla ciężkiego ekranu

Jeśli pierwszy ekran zawiera złożony układ (mapa, gradient, kilka list), używaj AsyncLayoutInflater do rozwijania ciężkich elementów w tle. Podczas rozwijania układu pokaż placeholder z efektem shimmer. Jest to szczególnie ważne dla Warm Start, gdzie każda milisekunda się liczy. AsyncLayoutInflater działa w wątku tła i przekazuje gotowy View w callbacku do głównego wątku.

kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Placeholder-makieta dla natychmiastowego renderowania
        setContentView(R.layout.placeholder_shimmer)

        // Asynchroniczne ładowanie ciężkiego układu
        AsyncLayoutInflater(this).inflate(
            R.layout.activity_main_complex,
            findViewById(R.id.container)
        ) { view, resId, parent ->
            parent?.removeAllViews()
            parent?.addView(view)
        }
    }
}

Często zadawane pytania

Czy Warm Start może przejść w Cold Start?

Tak, jeśli w momencie Warm Start system zdecyduje się zabić proces aplikacji (na przykład, aby zwolnić pamięć dla innej aplikacji), uruchomienie stanie się Cold Start od zera. Zdarza się to na urządzeniach z 2–3 GB RAM przy jednoczesnej pracy kilku aplikacji. Faktycznie Warm Start jest gwarantowany tylko w ciągu 10–20 minut po zminimalizowaniu na urządzeniach średniej klasy.

Czy ViewModel jest zachowywany przy Warm Start?

Tak, jeśli proces nie został zabity, ViewModel pozostaje w pamięci i onCleared nie jest wywoływane. To kluczowa zaleta Warm Start: wszystkie dane, załadowane zapytania sieciowe, pamięć podręczna w ViewModel — są dostępne natychmiast. Jeśli proces został zabity, ViewModel jest tworzony od nowa przez ViewModelProvider.Factory lub @HiltViewModel, a SavedStateHandle przywraca zapisane pola.

Dlaczego Warm Start może być wolniejszy niż Cold Start?

Teoretycznie Warm Start jest zawsze szybszy od Cold Start, ale w praktyce istnieją scenariusze, gdy różnica jest minimalna: jeśli Application.onCreate był lekki (50 ms), a Activity.onCreate — ciężki (800 ms), to Warm Start (800 ms) jest prawie równy Cold Start (850 ms). W tym przypadku optymalizować należy nie Application, a Activity.onCreate — to właśnie on staje się wąskim gardłem dla Warm Start.

Jak SplashScreen API wpływa na Warm Start?

SplashScreen API na Android 12+ wyświetla systemowy splash (ikonę na kolorowym tle) natychmiast po uruchomieniu — zarówno dla Cold, jak i Warm Start. Dla Warm Start splash wyświetla się tylko 100–300 ms, po czym zastępuje go UI aplikacji. SplashScreen nie przyspiesza samego uruchomienia, ale maskuje czas tworzenia Activity, poprawiając odbiór.

Czy trzeba optymalizować Warm Start, jeśli Cold Start jest już szybki?

Tak, ponieważ Warm Start zdarza się 2–3 razy częściej niż Cold Start. Jeśli Cold Start trwa 1.2 sekundy, a Warm Start — 600 ms, to 40% uruchomień (Warm) wciąż zajmuje 0.6 sekundy, co jest odczuwalne. Optymalizacja Warm Start do 200–300 ms daje użytkownikowi wrażenie natychmiastowego powrotu. Na urządzeniach z 6+ GB RAM Warm Start może stanowić do 80% wszystkich uruchomień, a jego optymalizacja staje się priorytetem.

Podsumowanie

  • Warm Start — uruchomienie aplikacji z istniejącym procesem, bez Activity w pamięci, czas 200–800 ms
  • Główna różnica od Cold Start: Application.onCreate nie jest wykonywane, klasy załadowane
  • Trzy fazy Warm Start: okno startowe → tworzenie Activity → pierwsza klatka
  • Mierzone przez ADB bez flagi -S lub Macrobenchmark z StartupMode.WARM
  • Optymalizacja: SavedStateHandle, AsyncLayoutInflater, cache-then-network, ConstraintLayout
  • ViewModel jest zachowywany przy Warm Start (proces żyje) — dane dostępne natychmiast
  • Warm Start stanowi 40–80% wszystkich uruchomień aplikacji

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.

Omów projekt

Przeczytaj również