Warm Start je scénář spuštění Android aplikace, při kterém proces aplikace již existuje v paměti (například po minimalizaci), ale Activity byla systémem zničena kvůli úspoře zdrojů. Application.onCreate již byl proveden, třídy jsou načteny, ale UI je vytvářeno znovu. Podle Google, 2024 trvá Warm Start 200 až 800 ms a tvoří přibližně 40 % všech spuštění na zařízeních se 4 GB RAM.
Hlavní body
Warm Start (teplý start) je stav mezi Cold Start a Hot Start: proces aplikace existuje v paměti (někdy v Linuxové mezipaměti na pozadí), ale Activity není aktivní a bude vytvořena znovu. Systém Android při nedostatku RAM může uvolnit Activity ze zásobníku, přičemž proces zůstane živý. Když se uživatel vrátí do aplikace, začne Warm Start: vytvoří se nová instance Activity, provedou se metody životního cyklu onCreate → onStart → onResume, ale Application.onCreate a načítání tříd se přeskočí.
Systém Android rozhoduje o uvolnění Activity na základě priority procesu (importance rank). Activity na pozadí (úroveň PROCESS_STATE_IMPORTANT_FOREGROUND nebo PROCESS_STATE_TOP_SLEEPING) může být zničena 5–30 minut po minimalizaci aplikace, v závislosti na dostupné RAM. Na zařízeních se 3 GB RAM může být Activity uvolněna po 10 minutách, na zařízeních s 8 GB — po několika hodinách. Důležité: při Warm Start se onSaveInstanceState volá před zničením Activity a vývojář může uložit stav UI.
Uživatel nevidí rozdíl mezi Warm a Cold Start — jen klikne na ikonu aplikace a čeká. Při Warm Start se však může objevit bílá obrazovka (blank window), pokud aplikace nenastavila vlastní téma pro spouštěcí okno. Google doporučuje nastavit vlastní téma v manifestu (Theme.AppCompat.Light nebo Theme.Material3.DayNight) pro spouštěcí Activity, aby se předešlo blikání bílé/černé obrazovky při Warm Start. Na Android 12+ toto efekty skrývá také SplashScreen API.
Pochopení rozdílu mezi třemi typy spuštění je nezbytné pro výběr správné strategie profilování a optimalizace. Každý typ má svou dobu trvání, svá úzká místa a své měřicí nástroje.
| Kritérium | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| Proces | Vytváří se znovu | Existuje v paměti | Existuje v paměti |
| Application.onCreate | Provádí se | Neprovádí se | Neprovádí se |
| Activity | Vytváří se od začátku | Vytváří se od začátku | Obnovuje se ze zásobníku |
| Čas | 1–5 sekund | 200–800 ms | < 200 ms |
| onCreate Activity | Plný | Plný (s restore) | Přeskakuje se |
V praxi tvoří Warm Start 30 % až 60 % všech spuštění aplikace, v závislosti na zvycích uživatele a množství RAM zařízení. Uživatelé, kteří drží mnoho aplikací otevřených (multitasker), se s Warm Start setkávají častěji. Pro sociální sítě a messengery je Warm Start nejběžnějším scénářem, protože aplikace je stále na pozadí. Pro bankovní aplikace naopak převládá Cold Start (nucené čištění procesu z bezpečnostních důvodů).
Warm Start se skládá ze tří fází, z nichž každá může být měřena a optimalizována. Na rozdíl od Cold Start zde není fáze fork a načítání tříd, ale je zde fáze obnovení stavu (restore), která může být nákladná.
Systém kontroluje, zda má aplikace téma pro spouštěcí okno. Pokud téma není nastaveno, zobrazí se bílá (nebo černá, v závislosti na systému) obrazovka. Pokud je téma nastaveno, zobrazí se pozadí z tématu. Tato fáze trvá 10–30 ms, ale je vizuálně znatelná, pokud téma neodpovídá skutečnému UI aplikace. Použijte Theme.Material3.DayNight s vlastním windowBackground, jehož barva odpovídá pozadí první obrazovky — to vytváří efekt okamžitého načítání.
Systém volá onCreate s předáním Bundle savedInstanceState, který byl uložen v onSaveInstanceState před zničením Activity. Pokud aplikace správně uložila stav (text polí, pozici posouvání, data ViewModel), obnovení proběhne rychle. Pokud ne — Activity začíná od prázdné stránky a uživatel vidí loader, dokud se data nenačtou. Klíčový bod: objekty ViewModel přežijí Warm Start pouze v případě, že proces nebyl zničen — při Warm Start zůstává ViewModel v paměti.
Po onCreate se provede onStart → onResume a systém zavolá první vykreslení. TTFD (Time To First Draw) pro Warm Start by měl být méně než 300 ms na průměrném zařízení. Pokud první obrazovka obsahuje složitý RecyclerView s těžkými View nebo načítá obrázky přes síť, TTFD může překročit práh. Použijte Placeholder a Shimmer pro plynulé načítání obsahu po prvním snímku.
Měření Warm Start je složitější než Cold Start, protože musíte simulovat stav «proces žije, Activity zničena». Standardní příkaz ADB s příznakem -S není vhodný — zabíjí proces. Pro Warm Start použijte jiné přístupy.
Nejprve spusťte aplikaci přes adb shell monkey nebo klepnutím na ikonu, poté ji minimalizujte (adb shell input keyevent 3 keyevent HOME). Počkejte 5–10 sekund, aby systém mohl uvolnit Activity, a spusťte adb shell am start -W (bez -S). Příkaz vrátí čas spuštění, který bude kratší než Cold Start. Pro reprodukovatelnost použijte skript: spustit → počkat → home → počkat → spustit.
# Simulace Warm Start přes ADB
$ adb shell am start -W \
com.example.app/.MainActivity
# Výstup (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
Knihovna androidx.benchmark.macro podporuje měření Warm Start. Pro to v testu nastavte startupMode = StartupMode.WARM — knihovna spustí aplikaci, minimalizuje ji, počká (konfigurovatelné zpoždění) a poté změří opětovné spuštění. Macrobenchmark provede 10–20 běhů a vypočítá percentily. V CI/CD můžete nastavit práh: pokud P50 Warm Start přesáhne 600 ms — test selže. To umožňuje sledovat regrese při každém commitu.
Firebase automaticky rozlišuje Cold a Warm Start na základě času od předchozího zavření aplikace. Pokud byla aplikace otevřena během posledních 30 minut, Firebase klasifikuje spuštění jako Warm. V konzoli Firebase uvidíte samostatné grafy pro každý typ spuštění, což umožňuje vyhodnotit účinnost optimalizací. Například po implementaci ukládání stavu do ViewModel můžete vidět snížení Warm Start až o 30 %.
Optimalizace Warm Start se zaměřuje na dva směry: zrychlení Activity.onCreate a správné obnovení stavu. Protože Application.onCreate a načítání tříd již byly provedeny, hlavním úzkým místem je UI kód první obrazovky.
Pokud uložený stav (savedInstanceState) obsahuje data, která je třeba deserializovat (Bitmap, String, JSON), proveďte to na vlákně na pozadí. Místo přímého čtení z Bundle v onCreate spusťte coroutine a zobrazte shimmer obrazovku. V praxi trvá deserializace Bundle na průměrném zařízení 20–100 ms — zdá se málo, ale pro Warm Start je to 10–50 % celkového času. Použijte Saved State Module knihovny Jetpack, který automaticky ukládá a obnovuje stav ViewModel v Bundle nebo databázi.
Rozbalení XML rozvržení (layout inflation) je jednou z nejdražších fází Warm Start. Pokud první obrazovka používá složitý CoordinatorLayout s AppBar, CollapsingToolbar, NestedScrollView a třemi RecyclerView, čas inflation může dosáhnout 300 ms. Řešení: použijte ConstraintLayout pro plochou hierarchii, aplikujte ViewStub pro sekce neviditelné při startu (bottom sheet, dialog), zapněte asynchronní rozbalení pro těžké fragmenty přes AsyncLayoutInflater. V Jetpack Compose není inflation potřeba, ale kompilace Compose stromu při Warm Start může zabrat podobný čas.
Při Warm Start mohou být data, která aplikace načetla v předchozí relaci, již v mezipaměti: databáze Room, SharedPreferences, in-memory cache ve ViewModel. Pokud vaše první obrazovka zobrazuje seznam ze serveru, zkontrolujte mezipaměť při startu a aktualizujte data na pozadí. Použijte strategii cache-then-network: nejprve zobrazte data z mezipaměti (okamžitě), poté aktualizujte ze serveru (asynchronně). Tím se zkrátí vnímaný čas Warm Start na 100–200 ms.
// ViewModel s mezipamětí pro Warm Start
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// Nejprve mezipaměť, pak síť
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start: data již v databázi
cache.emit(api.fetchItems()) // Aktualizace na pozadí
}
}
}
Správné uložení stavu je klíčovým faktorem, který odlišuje dobrý Warm Start od špatného. Uživatel očekává, že se vrátí do aplikace a uvidí totéž, co opustil — včetně pozice posouvání, textu v polích, vybraných záložek.
Systém volá onSaveInstanceState při zničení Activity, ale NEŽ může být proces zabit. V Bundle se ukládají pouze jednoduchá data (String, Int, Parcelable, Serializable). Pro složitá data použijte SavedStateHandle ve ViewModel — automaticky ukládá a obnovuje pole při Warm Start. Na rozdíl od onSaveInstanceState, SavedStateHandle funguje, i když proces přežil Warm Start (ViewModel není zničen). Příklad: pro text v EditText použijte SavedStateHandle.getLiveData(„text“) — text se automaticky uloží a obnoví.
Pokud při Warm Start nebyl proces zabit, ViewModel zůstává v paměti a onCleared se nevolá. To znamená, že všechna data načtená v předchozí relaci jsou okamžitě k dispozici. Pokud však byl proces zabit (zařízení v deep sleep déle než 30 minut), ViewModel je zničen a znovu vytvořen s SavedStateHandle. Pro správnou funkci ViewModel při Warm Start použijte SavedStateHandle s poli, která je třeba obnovit v jakémkoli scénáři. Rozdíl: ViewModel s @HiltViewModel podporuje SavedStateHandle automaticky.
| Mechanismus | Proces žije | Proces zabit |
|---|---|---|
| ViewModel | Data v paměti | Zničen, znovu vytvořen |
| SavedStateHandle | Data v paměti | Obnoveno z Bundle |
| onSaveInstanceState | Voláno při uvolnění Activity | Nevolá se |
| Room DB | Mezipaměť dostupná | Mezipaměť dostupná (disk) |
Jedním z nejčastějších problémů Warm Start je ztráta pozice posouvání. Uživatel posouval feed na 50. prvek, minimalizoval aplikaci, vrátil se — a vidí začátek seznamu. Řešení: uložte layoutManager.onSaveInstanceState (ukládá pozici a offset prvního viditelného prvku) a obnovte jej v onRestoreInstanceState. Poslední viditelnou pozici lze také uložit do SharedPreferences s klíčem datum/čas pro rychlé obnovení pozice při Warm Start.
// Uložení pozice posouvání 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) }
}
Dva praktické příklady optimalizace Warm Start: použití SavedStateHandle ve ViewModel a asynchronní obnovení složitých dat po spuštění.
SavedStateHandle automaticky ukládá pole do Bundle a obnovuje je při Warm Start. Pole profilu uživatele (String, JSON) bude obnoveno bez dalších požadavků na server. Pokud byl proces zabit, SavedStateHandle načte poslední uložený stav z Bundle.
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 není null, UI bez loaderu
// Po načtení: profile se aktualizuje v SavedStateHandle
Pokud první obrazovka obsahuje složité rozvržení (mapa, gradient, několik seznamů), použijte AsyncLayoutInflater pro rozbalení těžkých prvků na pozadí. Zatímco se rozvržení rozbaluje, zobrazte placeholder s efektem shimmer. To je zvláště důležité pro Warm Start, kde každá milisekunda záleží. AsyncLayoutInflater pracuje na vlákně na pozadí a doručí hotové View přes callback do hlavního vlákna.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Placeholder rozvržení pro okamžité vykreslení
setContentView(R.layout.placeholder_shimmer)
// Asynchronní načítání těžkého rozvržení
AsyncLayoutInflater(this).inflate(
R.layout.activity_main_complex,
findViewById(R.id.container)
) { view, resId, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
}
}
Často kladené otázky
Ano, pokud v okamžiku Warm Start systém rozhodne zabít proces aplikace (například pro uvolnění paměti pro jinou aplikaci), spuštění se stane Cold Start od začátku. K tomu dochází na zařízeních s 2–3 GB RAM při současném běhu několika aplikací. V praxi je Warm Start zaručen pouze 10–20 minut po minimalizaci na zařízeních střední třídy.
Ano, pokud proces nebyl zabit, ViewModel zůstává v paměti a onCleared se nevolá. To je klíčová výhoda Warm Start: všechna načtená data, síťové požadavky, mezipaměť ve ViewModel — okamžitě k dispozici. Pokud byl proces zabit, ViewModel je znovu vytvořen přes ViewModelProvider.Factory nebo @HiltViewModel a SavedStateHandle obnoví uložená pole.
Teoreticky je Warm Start vždy rychlejší než Cold Start, ale v praxi existují scénáře, kdy je rozdíl minimální: pokud je Application.onCreate lehký (50 ms) a Activity.onCreate těžký (800 ms), pak Warm Start (800 ms) je téměř roven Cold Start (850 ms). V tomto případě je třeba optimalizovat nikoli Application, ale Activity.onCreate — právě ten se stává úzkým hrdlem Warm Start.
SplashScreen API na Android 12+ zobrazuje systémový splash (ikonu na barevném pozadí) okamžitě při startu — pro Cold i Warm Start. Pro Warm Start se splash zobrazí pouze 100–300 ms, poté je nahrazen UI aplikace. SplashScreen sám o sobě start nezrychluje, ale maskuje čas vytváření Activity, čímž zlepšuje vnímání.
Ano, protože Warm Start se vyskytuje 2–3krát častěji než Cold Start. Pokud Cold Start trvá 1,2 sekundy a Warm Start 600 ms, pak 40 % spuštění (Warm) stále trvá 0,6 sekundy, což je znatelné. Optimalizace Warm Start na 200–300 ms dává uživateli pocit okamžitého návratu. Na zařízeních s 6+ GB RAM může Warm Start tvořit až 80 % všech spuštění a jeho optimalizace se stává prioritou.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také