Warm Start egy Android-alkalmazás indítási forgatókönyve, ahol az alkalmazás folyamata már létezik a memóriában (például minimalizálás után), de az Activity-t a rendszer megsemmisítette az erőforrások megtakarítása érdekében. Az Application.onCreate már lefutott, az osztályok betöltődtek, de az UI újra létrejön. A Google, 2024 szerint a Warm Start 200–800 ms-ig tart, és a 4 GB RAM-mal rendelkező eszközökön az összes indítás körülbelül 40%-át teszi ki.
Főbb pontok
Warm Start (meleg indítás) a Cold Start és Hot Start közötti állapot: az alkalmazás folyamata létezik a memóriában (néha a Linux háttér-gyorsítótárban), de az Activity nem aktív, és újra létrejön. Az Android rendszer RAM hiányában kitöltheti az Activity-t a veremből, miközben a folyamat életben marad. Amikor a felhasználó visszatér az alkalmazáshoz, elindul a Warm Start: létrejön az Activity új példánya, lefutnak az életciklus-metódusok onCreate → onStart → onResume, de az Application.onCreate és az osztályok betöltése kimarad.
Az Android rendszer a folyamat prioritása (importance rank) alapján dönt az Activity kitöltéséről. A háttérben lévő Activity (PROCESS_STATE_IMPORTANT_FOREGROUND vagy PROCESS_STATE_TOP_SLEEPING szint) 5–30 perccel az alkalmazás minimalizálása után megsemmisíthető, a rendelkezésre álló RAM-tól függően. A 3 GB RAM-mal rendelkező eszközökön az Activity 10 perc után kitölthető, a 8 GB-os eszközökön — órák után. Fontos: Warm Start esetén az onSaveInstanceState az Activity megsemmisítése előtt kerül meghívásra, és a fejlesztő elmentheti az UI állapotát.
A felhasználó nem lát különbséget a Warm és Cold Start között — csak megnyomja az alkalmazás ikonját és vár. Azonban Warm Start esetén megjelenhet egy fehér képernyő (blank window), ha az alkalmazás nem állított be saját témát a kezdő ablakhoz. A Google azt javasolja, hogy állítson be egyedi témát a manifest-ben (Theme.AppCompat.Light vagy Theme.Material3.DayNight) a kezdő Activity-hez a fehér/fekete képernyő villogásának elkerülése érdekében Warm Start esetén. Android 12+ rendszeren a SplashScreen API is elrejti ezt a hatást.
A három indítási típus közötti különbség megértése szükséges a megfelelő profilozási és optimalizálási stratégia kiválasztásához. Minden típusnak megvan a saját időtartama, saját szűk keresztmetszetei és saját mérőeszközei.
| Kritérium | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| Folyamat | Újra létrejön | Létezik a memóriában | Létezik a memóriában |
| Application.onCreate | Lefut | Nem fut le | Nem fut le |
| Activity | Nulláról létrejön | Nulláról létrejön | Visszaáll a veremből |
| Idő | 1–5 másodperc | 200–800 ms | < 200 ms |
| onCreate Activity | Teljes | Teljes (restore-al) | Kimarad |
A gyakorlatban a Warm Start az összes alkalmazásindítás 30–60%-át teszi ki, a felhasználói szokásoktól és az eszköz RAM mennyiségétől függően. Azok a felhasználók, akik sok alkalmazást tartanak nyitva (multitasker), gyakrabban találkoznak Warm Start-tal. A közösségi hálózatok és üzenetküldők esetében a Warm Start a leggyakoribb forgatókönyv, mivel az alkalmazás mindig a háttérben fut. A banki alkalmazásoknál ezzel szemben a Cold Start dominál (a folyamat kényszerű tisztítása biztonsági okokból).
A Warm Start három fázisból áll, amelyek mindegyike mérhető és optimalizálható. A Cold Start-tól eltérően itt nincs fork fázis és osztálybetöltés, de van állapot-visszaállítási fázis (restore), amely költséges lehet.
A rendszer ellenőrzi, hogy az alkalmazásnak van-e témája a kezdő ablakhoz. Ha nincs téma beállítva, fehér (vagy fekete, a rendszertől függően) képernyő jelenik meg. Ha van téma beállítva, a témából származó háttér jelenik meg. Ez a fázis 10–30 ms-ig tart, de vizuálisan érezhető, ha a téma nem egyezik az alkalmazás valós UI-jával. Használja a Theme.Material3.DayNight témát egyedi windowBackground-del, amelynek színe megegyezik az első képernyő hátterével — ez azonnali betöltés hatását kelti.
A rendszer meghívja az onCreate-t a Bundle savedInstanceState átadásával, amely az onSaveInstanceState-ban lett elmentve az Activity megsemmisítése előtt. Ha az alkalmazás helyesen mentette el az állapotot (mezőszöveg, görgetési pozíció, ViewModel adatok), a visszaállítás gyorsan megtörténik. Ha nem — az Activity üres lapról indul, és a felhasználó loader-t lát, amíg az adatok betöltődnek. Kulcspont: a ViewModel objektumok csak akkor élik túl a Warm Start-ot, ha a folyamat nem semmisült meg — Warm Start esetén a ViewModel a memóriában marad.
Az onCreate után lefut az onStart → onResume, és a rendszer meghívja az első megjelenítést. A TTFD (Time To First Draw) Warm Start esetén kevesebb mint 300 ms kell legyen egy közepes eszközön. Ha az első képernyő összetett RecyclerView-t tartalmaz nehéz View-kkal vagy képeket tölt be a hálózaton keresztül, a TTFD túllépheti a küszöbértéket. Használjon Placeholder-t és Shimmer-t a tartalom zökkenőmentes betöltéséhez az első képkocka után.
A Warm Start mérése bonyolultabb, mint a Cold Start-é, mert szimulálni kell a «a folyamat él, az Activity megsemmisült» állapotot. A szabványos ADB parancs a -S jelzővel nem megfelelő — megöli a folyamatot. Warm Start esetén más megközelítéseket használjon.
Először indítsa el az alkalmazást az adb shell monkey-n keresztül vagy koppintson az ikonra, majd minimalizálja (adb shell input keyevent 3 keyevent HOME). Várjon 5–10 másodpercet, hogy a rendszer kitölthesse az Activity-t, majd futtassa az adb shell am start -W parancsot (-S nélkül). A parancs visszaadja az indítási időt, amely rövidebb lesz, mint a Cold Start. A reprodukálhatóság érdekében használjon szkriptet: indítás → várakozás → home → várakozás → indítás.
# Warm Start szimulálása ADB-n keresztül
$ adb shell am start -W \
com.example.app/.MainActivity
# Kimenet (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
Az androidx.benchmark.macro könyvtár támogatja a Warm Start mérését. Ehhez a tesztben állítsa be a startupMode = StartupMode.WARM értéket — a könyvtár elindítja az alkalmazást, minimalizálja, vár (konfigurálható késleltetés), majd megméri az újraindítást. A Macrobenchmark 10–20 futtatást végez és percentiliseket számol. A CI/CD-ben küszöbértéket állíthat be: ha a P50 Warm Start meghaladja a 600 ms-t — a teszt megbukik. Ez lehetővé teszi a regressziók nyomon követését minden commit-nál.
A Firebase automatikusan megkülönbözteti a Cold és Warm Start-ot az alkalmazás előző bezárása óta eltelt idő alapján. Ha az alkalmazás az elmúlt 30 percben meg volt nyitva, a Firebase Warm-ként osztályozza az indítást. A Firebase konzolban külön grafikonokat fog látni minden indítási típushoz, lehetővé téve az optimalizálások hatékonyságának értékelését. Például az állapotmentés ViewModel-ben történő bevezetése után a Warm Start 30%-os csökkenését figyelheti meg.
A Warm Start optimalizálása két irányra összpontosít: az Activity.onCreate felgyorsítása és az állapot helyes visszaállítása. Mivel az Application.onCreate és az osztálybetöltés már megtörtént, a fő szűk keresztmetszet az első képernyő UI kódja.
Ha a mentett állapot (savedInstanceState) olyan adatokat tartalmaz, amelyeket deszerializálni kell (Bitmap, String, JSON), ezt háttérszálon végezze. Ahelyett, hogy közvetlenül a Bundle-ből olvasna az onCreate-ben, indítson el egy coroutine-t és jelenítsen meg egy shimmer képernyőt. A gyakorlatban a Bundle deszerializálása egy közepes eszközön 20–100 ms-ig tart — kevésnek tűnik, de Warm Start esetén ez a teljes idő 10–50%-a. Használja a Jetpack könyvtár Saved State Module modulját, amely automatikusan elmenti és visszaállítja a ViewModel állapotát Bundle-ben vagy adatbázisban.
Az XML elrendezés kiterjesztése (layout inflation) a Warm Start egyik legköltségesebb lépése. Ha az első képernyő összetett CoordinatorLayout-ot használ AppBar-rel, CollapsingToolbar-rel, NestedScrollView-val és három RecyclerView-val, az inflation idő elérheti a 300 ms-t. Megoldások: használjon ConstraintLayout-ot lapos hierarchiához, alkalmazza a ViewStub-ot a kezdetben nem látható szakaszokhoz (bottom sheet, dialog), kapcsolja be az aszinkron kiterjesztést nehéz fragmentumokhoz az AsyncLayoutInflater segítségével. A Jetpack Compose-ban nincs szükség inflation-re, de a Compose fa fordítása Warm Start esetén hasonló időt vehet igénybe.
Warm Start esetén az alkalmazás által az előző munkamenetben betöltött adatok már lehetnek a gyorsítótárban: Room adatbázis, SharedPreferences, in-memory cache a ViewModel-ben. Ha az első képernyő egy lista a szerverről, ellenőrizze a gyorsítótárat indításkor és frissítse az adatokat a háttérben. Használja a cache-then-network stratégiát: először jelenítse meg a gyorsítótárazott adatokat (azonnal), majd frissítse a szerverről (aszinkron). Ez csökkenti a Warm Start észlelt idejét 100–200 ms-ra.
// ViewModel gyorsítótárazással Warm Start-hoz
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// Először gyorsítótár, aztán hálózat
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start: adatok már az adatbázisban
cache.emit(api.fetchItems()) // Frissítés a háttérben
}
}
}
Az állapot helyes mentése az a kulcstényező, amely megkülönbözteti a jó Warm Start-ot a rossztól. A felhasználó azt várja, hogy visszatérve az alkalmazásba ugyanazt lássa, amit elhagyott — beleértve a görgetési pozíciót, a mezők szövegét, a kiválasztott lapokat.
A rendszer az onSaveInstanceState-t hívja meg az Activity megsemmisítésekor, de MIELŐTT a folyamat megölhető. A Bundle-ben csak egyszerű adatok (String, Int, Parcelable, Serializable) kerülnek mentésre. Összetett adatokhoz használja a SavedStateHandle-t a ViewModel-ben — automatikusan elmenti és visszaállítja a mezőket Warm Start esetén. Az onSaveInstanceState-tól eltérően a SavedStateHandle akkor is működik, ha a folyamat túlélte a Warm Start-ot (a ViewModel nem semmisül meg). Példa: az EditText szövegéhez használja a SavedStateHandle.getLiveData(„text“) hívást — a szöveg automatikusan mentésre és visszaállításra kerül.
Ha Warm Start esetén a folyamat nem ölődött meg, a ViewModel a memóriában marad és az onCleared nem hívódik meg. Ez azt jelenti, hogy az előző munkamenetben betöltött összes adat azonnal elérhető. De ha a folyamat megölték (az eszköz 30 percnél tovább deep sleep állapotban volt), a ViewModel megsemmisül és a SavedStateHandle segítségével jön létre újra. A ViewModel helyes működéséhez Warm Start esetén használjon SavedStateHandle-t olyan mezőkkel, amelyeket bármilyen forgatókönyv esetén vissza kell állítani. Különbség: a @HiltViewModel annotációval ellátott ViewModel automatikusan támogatja a SavedStateHandle-t.
| Mechanizmus | Folyamat él | Folyamat megölt |
|---|---|---|
| ViewModel | Adatok a memóriában | Megsemmisült, újra létrejön |
| SavedStateHandle | Adatok a memóriában | Visszaáll a Bundle-ből |
| onSaveInstanceState | Activity kitöltésekor hívódik | Nem hívódik |
| Room DB | Gyorsítótár elérhető | Gyorsítótár elérhető (lemez) |
A Warm Start egyik leggyakoribb problémája — a görgetési pozíció elvesztése. A felhasználó az 50. elemig görgette a hírfolyamot, minimalizálta az alkalmazást, visszatért — és a lista elejét látja. Megoldás: mentse el a layoutManager.onSaveInstanceState-t (megőrzi az első látható elem pozícióját és offset-jét), és állítsa vissza az onRestoreInstanceState-ben. Az utolsó látható pozíciót elmentheti a SharedPreferences-ben dátum/idő kulccsal is, hogy Warm Start esetén gyorsan visszaállítsa a pozíciót.
// RecyclerView görgetési pozíciójának mentése
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) }
}
Két gyakorlati példa a Warm Start optimalizálására: a SavedStateHandle használata a ViewModel-ben és az összetett adatok aszinkron visszaállítása az indítás után.
A SavedStateHandle automatikusan elmenti a mezőket a Bundle-ben és visszaállítja őket Warm Start esetén. A felhasználói profil mező (String, JSON) további szerverlekérdezések nélkül áll vissza. Ha a folyamatot megölték, a SavedStateHandle betölti az utolsó mentett állapotot a Bundle-ből.
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: profile nem null, UI loader nélkül
// Betöltés után: profile frissül a SavedStateHandle-ben
Ha az első képernyő összetett elrendezést (térkép, gradients, több lista) tartalmaz, használja az AsyncLayoutInflater-t a nehéz elemek háttérben történő kiterjesztésére. Amíg az elrendezés kiterjesztésre kerül, jelenítsen meg egy placeholder-t shimmer effektussal. Ez különösen fontos Warm Start esetén, ahol minden ezredmásodperc számít. Az AsyncLayoutInflater háttérszálon dolgozik, és a kész View-t callback-en keresztül juttatja el a főszálra.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Placeholder elrendezés azonnali megjelenítéshez
setContentView(R.layout.placeholder_shimmer)
// Nehéz elrendezés aszinkron betöltése
AsyncLayoutInflater(this).inflate(
R.layout.activity_main_complex,
findViewById(R.id.container)
) { view, resId, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
}
}
Gyakran ismételt kérdések
Igen, ha a Warm Start pillanatában a rendszer úgy dönt, hogy megöli az alkalmazás folyamatát (például memória felszabadítása céljából egy másik alkalmazás számára), az indítás nulláról Cold Start-tá válik. Ez 2–3 GB RAM-mal rendelkező eszközökön fordul elő, amikor több alkalmazás egyidejűleg fut. A gyakorlatban a Warm Start csak 10–20 percig garantált a minimalizálás után középkategóriás eszközökön.
Igen, ha a folyamat nem ölődött meg, a ViewModel a memóriában marad és az onCleared nem hívódik meg. Ez a Warm Start kulcsfontosságú előnye: az összes betöltött adat, hálózati kérés, ViewModel-ben lévő gyorsítótár — azonnal elérhető. Ha a folyamatot megölték, a ViewModel a ViewModelProvider.Factory vagy @HiltViewModel segítségével jön létre újra, és a SavedStateHandle visszaállítja a mentett mezőket.
Elméletileg a Warm Start mindig gyorsabb, mint a Cold Start, de a gyakorlatban vannak olyan forgatókönyvek, ahol a különbség minimális: ha az Application.onCreate könnyű (50 ms) és az Activity.onCreate nehéz (800 ms), akkor a Warm Start (800 ms) majdnem egyenlő a Cold Start-tal (850 ms). Ebben az esetben nem az Application-t, hanem az Activity.onCreate-t kell optimalizálni — ez válik a Warm Start szűk keresztmetszetévé.
Az Android 12+ rendszerben a SplashScreen API azonnal megjeleníti a rendszer splash-t (ikon színes háttérrel) az indításkor — mind Cold, mind Warm Start esetén. Warm Start esetén a splash csak 100–300 ms-ig látszik, majd az alkalmazás UI-ja váltja fel. A SplashScreen maga nem gyorsítja fel az indítást, de elrejti az Activity létrehozásának idejét, javítva az észlelést.
Igen, mert a Warm Start 2–3-szor gyakrabban fordul elő, mint a Cold Start. Ha a Cold Start 1,2 másodpercig tart, a Warm Start pedig 600 ms, akkor az indítások 40%-a (Warm) még mindig 0,6 másodpercig tart, ami érezhető. A Warm Start 200–300 ms-ra optimalizálása azonnali visszatérés érzését adja a felhasználónak. A 6+ GB RAM-mal rendelkező eszközökön a Warm Start az összes indítás akár 80%-át is kiteheti, és optimalizálása prioritássá válik.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is