Warm Start — este un scenariu de pornire a aplicației Android în care procesul aplicației există deja în memorie (de exemplu, după minimizare), dar Activity a fost distrusă de sistem pentru economisirea resurselor. Application.onCreate a fost deja executat, clasele sunt încărcate, dar UI este creat din nou. Conform Google, 2024, Warm Start durează între 200 și 800 ms și reprezintă aproximativ 40% din toate pornirile pe dispozitive cu 4 GB RAM.
Principalele
Warm Start (pornire la cald) — este o stare între Cold Start și Hot Start: procesul aplicației există în memorie (uneori în memoria cache de fundal a Linux), dar Activity nu este activă și va fi creată din nou. Sistemul Android, atunci când memoria RAM este insuficientă, poate elibera Activity din stivă, lăsând procesul în viață. Când utilizatorul revine la aplicație, pornește Warm Start: se creează o nouă instanță Activity, se execută metodele ciclului de viață onCreate → onStart → onResume, dar Application.onCreate și încărcarea claselor sunt omise.
Sistemul Android ia decizia de a elibera Activity pe baza priorității procesului (importance rank). Activity în fundal (nivel PROCESS_STATE_IMPORTANT_FOREGROUND sau PROCESS_STATE_TOP_SLEEPING) poate fi distrusă după 5–30 de minute de la minimizarea aplicației, în funcție de RAM-ul disponibil. Pe dispozitivele cu 3 GB RAM, Activity poate fi eliberată după doar 10 minute, pe dispozitivele cu 8 GB — după câteva ore. Important: la Warm Start, onSaveInstanceState este apelat înainte de distrugerea Activity, iar dezvoltatorul poate salva starea UI.
Utilizatorul nu vede diferența între Warm și Cold Start — el doar apasă pe iconița aplicației și așteaptă. Totuși, la Warm Start, ecranul alb (blank window) poate apărea dacă aplicația nu și-a configurat propria temă pentru fereastra de pornire. Google recomandă setarea unei teme personalizate în manifest (Theme.AppCompat.Light sau Theme.Material3.DayNight) pentru Activity de pornire, pentru a evita pâlpâirea ecranului alb/negru la Warm Start. Pe Android 12+, SplashScreen API ascunde de asemenea acest efect.
Înțelegerea diferenței dintre cele trei tipuri de pornire este necesară pentru alegerea strategiei corecte de profilare și optimizare. Fiecare tip are propria durată, propriile blocaje și propriile instrumente de măsurare.
| Criteriu | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| Proces | Creat din nou | Există în memorie | Există în memorie |
| Application.onCreate | Se execută | Nu se execută | Nu se execută |
| Activity | Creată de la zero | Creată de la zero | Restaurată din stivă |
| Timp | 1–5 secunde | 200–800 ms | < 200 ms |
| onCreate Activity | Complet | Complet (cu restaurare) | Omis |
În practică, Warm Start constituie de la 30% la 60% din toate pornirile aplicației, în funcție de obiceiurile utilizatorului și cantitatea de RAM a dispozitivului. Utilizatorii care țin multe aplicații deschise (multitasker) se confruntă mai des cu Warm Start. Pentru rețelele sociale și mesagerie, Warm Start este cel mai frecvent scenariu, deoarece aplicația este tot timpul în fundal. Pentru aplicațiile bancare, dimpotrivă, predomină Cold Start (îpărtarea forțată a procesului din motive de securitate).
Warm Start constă din trei faze, fiecare dintre ele putând fi măsurată și optimizată. Spre deosebire de Cold Start, aici nu există faza de fork și încărcare a claselor, dar există o fază de restaurare a stării (restore) care poate fi costisitoare.
Sistemul verifică dacă aplicația are o temă pentru fereastra de pornire. Dacă tema nu este setată, se afișează un ecran alb (sau negru, în funcție de sistem). Dacă tema este setată, se afișează fundalul din temă. Această fază durează 10–30 ms, dar este vizibilă dacă tema nu se potrivește cu UI-ul real al aplicației. Folosiți Theme.Material3.DayNight cu un windowBackground personalizat a cărui culoare coincide cu fundalul primului ecran — aceasta creează un efect de încărcare instantanee.
Sistemul apelează onCreate cu transmiterea Bundle savedInstanceState care a fost salvat în onSaveInstanceState înainte de distrugerea Activity. Dacă aplicația a salvat corect starea (textul câmpurilor, poziția de derulare, datele ViewModel), restaurarea are loc rapid. În caz contrar — Activity începe de la o pagină curată, iar utilizatorul vede un loader până când datele se încarcă. Momentul cheie: obiectele ViewModel supraviețuiesc Warm Start doar dacă procesul nu a fost distrus — la Warm Start, ViewModel rămâne în memorie.
După onCreate se execută onStart → onResume, iar sistemul apelează primul randament. TTFD (Time To First Draw) pentru Warm Start trebuie să fie sub 300 ms pe un dispozitiv mediu. Dacă primul ecran conține un RecyclerView complex cu View-uri grele sau încarcă imagini din rețea, TTFD poate depăși pragul. Folosiți Placeholder și Shimmer pentru încărcarea lină a conținutului după primul cadru.
Măsurarea Warm Start este mai dificilă decât Cold Start, deoarece trebuie să simulați starea „proces viu, Activity distrusă”. Comanda standard ADB cu flag-ul -S nu este potrivită — omoară procesul. Pentru Warm Start, folosiți alte abordări.
Mai întâi, porniți aplicația prin adb shell monkey sau atingând iconița, apoi minimizați-o (adb shell input keyevent 3 keyevent HOME). Așteptați 5–10 secunde pentru ca sistemul să poată elibera Activity și lansați adb shell am start -W (fără -S). Comanda va returna timpul de pornire, care va fi mai scurt decât Cold Start. Pentru reproductibilitate, folosiți un script: pornire → așteptare → home → așteptare → pornire.
# Simularea Warm Start prin ADB
$ adb shell am start -W \
com.example.app/.MainActivity
# Rezultat (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
Biblioteca androidx.benchmark.macro suportă măsurarea Warm Start. Pentru aceasta, în test setați startupMode = StartupMode.WARM — biblioteca va porni aplicația, o va minimiza, va aștepta (configurable delay) și apoi va măsura repornirea. Macrobenchmark face 10–20 de rulări și calculează percentilii. În CI/CD puteți seta un prag: dacă P50 Warm Start depășește 600 ms — testul eșuează. Aceasta permite urmărirea regresiunilor la fiecare commit.
Firebase face automat distincția între Cold și Warm Start pe baza timpului de la închiderea anterioară a aplicației. Dacă aplicația a fost deschisă în ultimele 30 de minute, Firebase clasifică pornirea ca Warm. În consola Firebase veți vedea grafice separate pentru fiecare tip de pornire, ceea ce permite evaluarea eficienței optimizărilor. De exemplu, după implementarea salvării stării în ViewModel, se poate observa o reducere de 30% a Warm Start.
Optimizarea Warm Start se concentrează pe două direcții: accelerarea Activity.onCreate și restaurarea corectă a stării. Deoarece Application.onCreate și încărcarea claselor sunt deja executate, blocajul principal este codul UI al primului ecran.
Dacă starea salvată (savedInstanceState) conține date care trebuie deserializate (Bitmap, String, JSON), faceți acest lucru în firul de fundal. În loc să citiți direct din Bundle în onCreate, lansați o corutină și afișați un ecran shimmer. În practică, deserializarea Bundle pe un dispozitiv mediu durează 20–100 ms — pare puțin, dar pentru Warm Start aceasta reprezintă 10–50% din timpul total. Folosiți Saved State Module al bibliotecii Jetpack, care salvează și restaurează automat starea ViewModel în Bundle sau baza de date.
Expandarea layout-ului XML (layout inflation) — una dintre cele mai costisitoare etape ale Warm Start. Dacă primul ecran folosește un CoordinatorLayout complex cu AppBar, CollapsingToolbar, NestedScrollView plus trei RecyclerView, timpul de inflation poate atinge 300 ms. Soluții: folosiți ConstraintLayout pentru o ierarhie plată, aplicați ViewStub pentru secțiunile invizibile la pornire (bottom sheet, dialog), activați expandarea asincronă pentru fragmentele grele prin AsyncLayoutInflater. În Jetpack Compose, inflation nu este necesar, dar compilarea arborelui Compose la Warm Start poate dura un timp similar.
La Warm Start, datele pe care aplicația le-a încărcat în sesiunea anterioară pot fi deja în cache: baza de date Room, SharedPreferences, memoria cache in-memory în ViewModel. Dacă primul ecran afișează o listă de pe server, verificați cache-ul la pornire și actualizați datele în fundal. Folosiți strategia cache-then-network: mai întâi afișați datele din cache (instantaneu), apoi actualizați de pe server (asincron). Aceasta reduce timpul perceput al Warm Start la 100–200 ms.
// ViewModel cu memorare în cache pentru Warm Start
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// Mai întâi cache, apoi rețeaua
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start: datele sunt deja în baza de date
cache.emit(api.fetchItems()) // Actualizare în fundal
}
}
}
Salvarea corectă a stării — factorul cheie care diferențiază un Warm Start bun de unul prost. Utilizatorul se așteaptă să revină în aplicație și să vadă același lucru pe care l-a lăsat — inclusiv poziția de derulare, textul în câmpuri, filele selectate.
Sistemul apelează onSaveInstanceState la distrugerea Activity, dar ÎNAINTE ca procesul să poată fi ucis. În Bundle se salvează doar date simple (String, Int, Parcelable, Serializable). Pentru date complexe, folosiți SavedStateHandle în ViewModel — acesta salvează și restaurează automat câmpurile la Warm Start. Spre deosebire de onSaveInstanceState, SavedStateHandle funcționează chiar dacă procesul a supraviețuit Warm Start (ViewModel nu este distrus). Exemplu: pentru textul în EditText, folosiți SavedStateHandle.getLiveData(“text”) — textul se va salva și restaura automat.
Dacă la Warm Start procesul nu a fost ucis, ViewModel rămâne în memorie și onCleared nu este apelat. Aceasta înseamnă că toate datele încărcate în sesiunea anterioară sunt disponibile instantaneu. Totuși, dacă procesul a fost ucis (dispozitivul în deep sleep mai mult de 30 de minute), ViewModel este distrus și creat din nou cu SavedStateHandle. Pentru funcționarea corectă a ViewModel la Warm Start, folosiți SavedStateHandle cu câmpuri care trebuie restaurate în orice scenariu. Diferența: ViewModel cu @HiltViewModel suportă SavedStateHandle automat.
| Mecanism | Proces viu | Proces ucis |
|---|---|---|
| ViewModel | Date în memorie | Distrus, creat din nou |
| SavedStateHandle | Date în memorie | Restaurate din Bundle |
| onSaveInstanceState | Apelat la eliberarea Activity | Nu este apelat |
| Room DB | Cache disponibil | Cache disponibil (disc) |
Una dintre cele mai frecvente probleme ale Warm Start — pierderea poziției de derulare. Utilizatorul derula lista până la al 50-lea element, a minimizat aplicația, s-a întors — și vede începutul listei. Soluția: salvați layoutManager.onSaveInstanceState (salvează poziția și offset-ul primului element vizibil) și restaurați-l în onRestoreInstanceState. De asemenea, puteți salva ultima poziție vizibilă în SharedPreferences cu o cheie după dată/oră pentru a restaura rapid poziția la Warm Start.
// Salvarea poziției de derulare 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) }
}
Două exemple practice de optimizare a Warm Start: utilizarea SavedStateHandle în ViewModel și restaurarea asincronă a datelor complexe după pornire.
SavedStateHandle salvează automat câmpurile în Bundle și le restaurează la Warm Start. Câmpul de profil al utilizatorului (String, JSON) va fi restaurat fără cereri suplimentare la server. Dacă procesul a fost ucis, SavedStateHandle va încărca ultima stare salvată din 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: profilul nu este null, UI fără loader
// După încărcare: profilul se actualizează în SavedStateHandle
Dacă primul ecran conține un layout complex (hartă, gradient, mai multe liste), folosiți AsyncLayoutInflater pentru expandarea elementelor grele în fundal. În timp ce layout-ul se expandează, afișați un placeholder cu efect shimmer. Acest lucru este deosebit de important pentru Warm Start, unde fiecare milisecundă contează. AsyncLayoutInflater funcționează pe firul de fundal și transmite View-ul gata în callback pe firul principal.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Mockup Placeholder pentru randare instantanee
setContentView(R.layout.placeholder_shimmer)
// Încărcarea asincronă a layout-ului greu
AsyncLayoutInflater(this).inflate(
R.layout.activity_main_complex,
findViewById(R.id.container)
) { view, resId, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
}
}
Întrebări frecvente
Da, dacă în momentul Warm Start sistemul decide să ucidă procesul aplicației (de exemplu, pentru a elibera memoria pentru o altă aplicație), pornirea va deveni Cold Start de la zero. Aceasta se întâmplă pe dispozitivele cu 2–3 GB RAM la lucrul simultan al mai multor aplicații. Practic, Warm Start este garantat doar în decurs de 10–20 de minute după minimizare pe dispozitivele de clasă medie.
Da, dacă procesul nu a fost ucis, ViewModel rămâne în memorie și onCleared nu este apelat. Acesta este avantajul cheie al Warm Start: toate datele încărcate, cererile de rețea, memoria cache în ViewModel — sunt disponibile instantaneu. Dacă procesul a fost ucis, ViewModel este creat din nou prin ViewModelProvider.Factory sau @HiltViewModel, iar SavedStateHandle restaurează câmpurile salvate.
Teoretic, Warm Start este întotdeauna mai rapid decât Cold Start, dar în practică există scenarii când diferența este minimă: dacă Application.onCreate a fost ușor (50 ms), iar Activity.onCreate — greu (800 ms), atunci Warm Start (800 ms) este aproape egal cu Cold Start (850 ms). În acest caz, trebuie optimizat nu Application, ci Activity.onCreate — tocmai el devine blocajul pentru Warm Start.
SplashScreen API pe Android 12+ afișează un splash de sistem (iconiță pe fundal colorat) imediat la pornire — atât pentru Cold, cât și pentru Warm Start. Pentru Warm Start, splash-ul se afișează doar 100–300 ms, după care este înlocuit de UI-ul aplicației. SplashScreen nu accelerează pornirea în sine, dar maschează timpul de creare a Activity, îmbunătățind percepția.
Da, deoarece Warm Start apare de 2–3 ori mai des decât Cold Start. Dacă Cold Start durează 1.2 secunde, iar Warm Start — 600 ms, atunci 40% din porniri (Warm) încă durează 0.6 secunde, ceea ce este sesizabil. Optimizarea Warm Start la 200–300 ms oferă utilizatorului senzația de revenire instantanee. Pe dispozitivele cu 6+ GB RAM, Warm Start poate constitui până la 80% din toate pornirile, iar optimizarea sa devine prioritară.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și