Warm Start: essenza, avvio a caldo e ottimizzazione in Android

Autore: IT Sectr Pubblicato: 2026-03-31 Tempo di lettura: 8 min

Warm Start è uno scenario di avvio di un'app Android in cui il processo dell'app esiste già in memoria (ad esempio, dopo essere stata minimizzata), ma l'Activity è stata distrutta dal sistema per risparmiare risorse. Application.onCreate è già stato eseguito, le classi sono caricate, ma l'UI viene creata di nuovo. Secondo Google, 2024, Warm Start richiede 200–800 ms e costituisce circa il 40% di tutti gli avvii su dispositivi con 4 GB di RAM.

Punti chiave

  • Warm Start — avvio dell'app con processo esistente, ma senza Activity in memoria
  • Differenza da Cold Start: Application.onCreate non viene eseguito, le classi sono già caricate
  • Tempo Warm Start richiede 200–800 ms contro 1–5 secondi per Cold Start
  • Scenari: ritorno all'app dopo diverse ore, rimozione dell'Activity da OOM-killer
  • Ottimizzazione si concentra sulla preservazione dello stato dell'Activity e sul caching dei dati

Cos'è Warm Start

Warm Start è uno stato tra Cold Start e Hot Start: il processo dell'app esiste in memoria (a volte nella cache di background di Linux), ma l'Activity non è attiva e verrà creata di nuovo. Quando Android esaurisce la RAM, può rimuovere l'Activity dallo stack, lasciando vivo il processo. Quando l'utente torna all'app, si verifica un Warm Start: viene creata una nuova istanza di Activity, i metodi del ciclo di vita onCreate → onStart → onResume vengono eseguiti, ma Application.onCreate e il caricamento delle classi vengono saltati.

Cause di Warm Start

Android decide di rimuovere l'Activity in base alla priorità del processo (rango di importanza). Un'Activity in background (livello PROCESS_STATE_IMPORTANT_FOREGROUND o PROCESS_STATE_TOP_SLEEPING) può essere distrutta 5–30 minuti dopo la minimizzazione dell'app, a seconda della RAM disponibile. Su dispositivi con 3 GB di RAM, l'Activity può essere rimossa entro 10 minuti; su dispositivi con 8 GB di RAM, dopo diverse ore. Importante: durante Warm Start, onSaveInstanceState viene chiamato prima che l'Activity venga distrutta e lo sviluppatore può salvare lo stato dell'UI.

Percezione dell'utente

L'utente non vede la differenza tra Warm e Cold Start — tocca semplicemente l'icona dell'app e aspetta. Tuttavia, durante Warm Start, potrebbe apparire una schermata bianca se l'app non ha impostato un tema personalizzato per la finestra di avvio. Google consiglia di impostare un tema personalizzato nel manifesto (Theme.AppCompat.Light o Theme.Material3.DayNight) per l'Activity di avvio, per evitare lo sfarfallio dello schermo bianco/nero durante Warm Start. Su Android 12+, l'API SplashScreen nasconde anche questo effetto.

Warm Start vs Cold Start vs Hot Start

Comprendere la differenza tra i tre tipi di avvio è essenziale per scegliere la giusta strategia di profilazione e ottimizzazione. Ogni tipo ha la propria durata, i propri colli di bottiglia e i propri strumenti di misurazione.

CriterioCold StartWarm StartHot Start
ProcessoCreato da zeroEsiste in memoriaEsiste in memoria
Application.onCreateEseguitoNon eseguitoNon eseguito
ActivityCreata da zeroCreata da zeroRipristinata dallo stack
Tempo1–5 secondi200–800 ms< 200 ms
Activity.onCreateCompletoCompleto (con ripristino)Saltato

In pratica, Warm Start rappresenta dal 30% al 60% di tutti gli avvii delle app, a seconda delle abitudini dell'utente e della RAM del dispositivo. Gli utenti che tengono molte app aperte (multitasking) incontrano Warm Start più spesso. Per i social network e i messaggeri, Warm Start è lo scenario più comune poiché l'app è sempre in background. Per le app bancarie, al contrario, predomina Cold Start (pulizia forzata del processo per motivi di sicurezza).

Fasi dell'avvio a caldo

Warm Start consiste in tre fasi, ciascuna può essere misurata e ottimizzata. A differenza di Cold Start, non c'è fase di fork o caricamento delle classi, ma c'è una fase di ripristino dello stato che può essere costosa.

Fase 1: Finestra di avvio (sfondo finestra)

Il sistema verifica se l'app ha un tema per la finestra di avvio. Se il tema non è impostato, viene visualizzata una schermata bianca (o nera, a seconda del sistema). Se il tema è impostato, viene mostrato lo sfondo del tema. Questa fase richiede 10–30 ms, ma è visivamente percepibile se il tema non corrisponde all'UI reale dell'app. Utilizza Theme.Material3.DayNight con un windowBackground personalizzato il cui colore corrisponde allo sfondo del primo schermo — questo crea un effetto di caricamento istantaneo.

Fase 2: Creazione dell'Activity (ripristino)

Il sistema chiama onCreate passando il Bundle savedInstanceState che è stato salvato in onSaveInstanceState prima che l'Activity fosse distrutta. Se l'app ha salvato correttamente lo stato (testo dei campi, posizione di scorrimento, dati ViewModel), il ripristino avviene rapidamente. In caso contrario, l'Activity inizia da zero e l'utente vede un caricatore mentre i dati vengono caricati. Punto chiave: gli oggetti ViewModel sopravvivono a Warm Start solo se il processo non è stato distrutto — durante Warm Start, il ViewModel rimane in memoria.

Fase 3: Primo fotogramma (TTFD)

Dopo onCreate, onStart → onResume vengono eseguiti e il sistema attiva il primo disegno. TTFD (Time To First Draw) per Warm Start dovrebbe essere inferiore a 300 ms su un dispositivo di fascia media. Se il primo schermo contiene una RecyclerView complessa con Views pesanti o carica immagini dalla rete, TTFD può superare la soglia. Utilizza Placeholder e Shimmer per un caricamento fluido del contenuto dopo il primo fotogramma.

Come misurare Warm Start

Misurare Warm Start è più complesso di Cold Start perché è necessario simulare lo stato in cui il processo è vivo ma l'Activity è distrutta. Il comando ADB standard con il flag -S non funziona — uccide il processo. Utilizza approcci diversi per Warm Start.

ADB shell am start senza -S

Prima, avvia l'app tramite adb shell monkey o tocca l'icona, poi minimizzala (adb shell input keyevent 3 keyevent HOME). Aspetta 5–10 secondi per consentire al sistema di rimuovere l'Activity, quindi esegui adb shell am start -W (senza -S). Il comando restituirà un tempo di avvio più breve rispetto a Cold Start. Per riproducibilità, usa uno script: avvia → attendi → home → attendi → avvia.

bash
# Simulazione di Warm Start tramite ADB
$ adb shell am start -W \
    com.example.app/.MainActivity

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

Macrobenchmark per Warm Start

La libreria androidx.benchmark.macro supporta la misurazione di Warm Start. Nel test, imposta startupMode = StartupMode.WARM — la libreria avvierà l'app, la minimizzerà, attenderà (ritardo configurabile) e poi misurerà il riavvio. Macrobenchmark esegue 10–20 iterazioni e calcola i percentili. In CI/CD, puoi impostare una soglia: se P50 Warm Start supera 600 ms, il test fallisce. Questo permette di tracciare le regressioni ad ogni commit.

Firebase Performance Monitoring

Firebase distingue automaticamente tra Cold e Warm Start in base al tempo dall'ultima chiusura dell'app. Se l'app è stata aperta negli ultimi 30 minuti, Firebase classifica l'avvio come Warm. Nella console Firebase, vedrai grafici separati per ogni tipo di avvio, permettendoti di valutare l'efficacia delle ottimizzazioni. Ad esempio, dopo aver implementato la preservazione dello stato in ViewModel, potresti vedere una riduzione del 30% del tempo di Warm Start.

Come ottimizzare Warm Start

L'ottimizzazione di Warm Start si concentra su due aree: accelerare Activity.onCreate e il corretto ripristino dello stato. Poiché Application.onCreate e il caricamento delle classi sono già stati completati, il principale collo di bottiglia è il codice UI del primo schermo.

Ripristino asincrono dello stato

Se lo stato salvato (savedInstanceState) contiene dati che necessitano di deserializzazione (Bitmap, String, JSON), fallo su un thread in background. Invece di leggere direttamente dal Bundle in onCreate, avvia una coroutine e mostra una schermata shimmer. In pratica, la deserializzazione del Bundle su un dispositivo di fascia media richiede 20–100 ms — sembra poco, ma per Warm Start è il 10–50% del tempo totale. Utilizza il Saved State Module di Jetpack, che salva e ripristina automaticamente lo stato del ViewModel nel Bundle o nel database.

Ottimizzazione di setContentView

L'inflazione del layout XML è una delle fasi più costose di Warm Start. Se il primo schermo utilizza un CoordinatorLayout complesso con AppBar, CollapsingToolbar, NestedScrollView più tre RecyclerView, il tempo di inflazione può raggiungere 300 ms. Soluzioni: usa ConstraintLayout per una gerarchia piatta, applica ViewStub per le sezioni non visibili all'avvio (bottom sheet, dialog), abilita l'inflazione asincrona per i fragment pesanti tramite AsyncLayoutInflater. In Jetpack Compose, l'inflazione non è necessaria, ma la compilazione dell'albero Compose durante Warm Start può richiedere un tempo simile.

Caching dei dati

Durante Warm Start, i dati che l'app ha caricato nella sessione precedente potrebbero essere già in cache: database Room, SharedPreferences, cache in memoria in ViewModel. Se il tuo primo schermo mostra un elenco dal server, controlla la cache all'avvio e aggiorna i dati in background. Usa la strategia cache-then-network: prima mostra i dati in cache (istantaneamente), poi aggiorna dal server (asincrono). Questo riduce il tempo percepito di Warm Start a 100–200 ms.

kotlin
// ViewModel con caching per Warm Start
class FeedViewModel : ViewModel() {
    private val cache = MutableStateFlow<List<Item>>(emptyList())

    init {
        // Prima cache, poi rete
        viewModelScope.launch {
            cache.emit(db.getItems()) // Warm Start: dati già nel DB
            cache.emit(api.fetchItems()) // Aggiornamento in background
        }
    }
}

Preservazione dello stato durante Warm Start

La corretta preservazione dello stato è il fattore chiave che distingue un buon Warm Start da uno cattivo. L'utente si aspetta di tornare all'app e vedere esattamente ciò che ha lasciato — inclusa la posizione di scorrimento, il testo nei campi e le schede selezionate.

onSaveInstanceState

Il sistema chiama onSaveInstanceState quando l'Activity viene distrutta, ma PRIMA che il processo possa essere ucciso. Solo i tipi di dati semplici (String, Int, Parcelable, Serializable) vengono salvati nel Bundle. Per dati complessi, usa SavedStateHandle in ViewModel — salva e ripristina automaticamente i campi durante Warm Start. A differenza di onSaveInstanceState, SavedStateHandle funziona anche se il processo sopravvive a Warm Start (ViewModel non viene distrutto). Esempio: per il testo in EditText, usa SavedStateHandle.getLiveData(“text”) — il testo verrà automaticamente salvato e ripristinato.

ViewModel e Warm Start

Se il processo non è stato ucciso durante Warm Start, il ViewModel rimane in memoria e onCleared non viene chiamato. Ciò significa che tutti i dati caricati nella sessione precedente sono immediatamente disponibili. Tuttavia, se il processo è stato ucciso (dispositivo in sospensione profonda per più di 30 minuti), il ViewModel viene distrutto e ricreato con SavedStateHandle. Per un corretto comportamento del ViewModel durante Warm Start, usa SavedStateHandle con i campi che devono essere ripristinati in qualsiasi scenario. Differenza: ViewModel con @HiltViewModel supporta SavedStateHandle automaticamente.

MeccanismoProcesso vivoProcesso ucciso
ViewModelDati in memoriaDistrutto, ricreato
SavedStateHandleDati in memoriaRipristinato da Bundle
onSaveInstanceStateChiamato alla rimozione dell'ActivityNon chiamato
Room DBCache disponibileCache disponibile (disco)

Salvare la posizione di scorrimento di RecyclerView

Uno dei problemi più comuni di Warm Start — perdere la posizione di scorrimento. L'utente ha scrollato fino al 50° elemento, minimizzato l'app, è tornato — e vede l'inizio della lista. Soluzione: salva layoutManager.onSaveInstanceState (salva la posizione e l'offset del primo elemento visibile) e ripristinalo in onRestoreInstanceState. Puoi anche salvare l'ultima posizione visibile in SharedPreferences con una chiave data/ora per ripristinare rapidamente la posizione durante Warm Start.

kotlin
// Salvare la posizione di scorrimento di 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) }
}

Esempi di codice per Warm Start

Due esempi pratici di ottimizzazione di Warm Start: uso di SavedStateHandle in ViewModel e ripristino asincrono di dati complessi dopo l'avvio.

ViewModel con SavedStateHandle

SavedStateHandle salva automaticamente i campi nel Bundle e li ripristina durante Warm Start. Il campo del profilo utente (String, JSON) verrà ripristinato senza richieste superflue al server. Se il processo è stato ucciso, SavedStateHandle carica l'ultimo stato salvato dal 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: profile non è null, UI senza caricatore
// Dopo il caricamento: profile si aggiorna in SavedStateHandle

AsyncLayoutInflater per schermi pesanti

Se il primo schermo contiene un layout complesso (mappa, gradiente, più elenchi), usa AsyncLayoutInflater per gonfiare elementi pesanti in background. Mentre il layout viene gonfiato, mostra un placeholder con effetto shimmer. Questo è particolarmente importante per Warm Start, dove ogni millisecondo conta. AsyncLayoutInflater viene eseguito su un thread in background e passa la View pronta a un callback sul thread principale.

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

        // Layout placeholder per rendering istantaneo
        setContentView(R.layout.placeholder_shimmer)

        // Caricamento asincrono del layout pesante
        AsyncLayoutInflater(this).inflate(
            R.layout.activity_main_complex,
            findViewById(R.id.container)
        ) { view, resId, parent ->
            parent?.removeAllViews()
            parent?.addView(view)
        }
    }
}

Domande frequenti

Warm Start può trasformarsi in Cold Start?

Sì, se al momento di Warm Start il sistema decide di uccidere il processo dell'app (ad esempio, per liberare memoria per un'altra app), l'avvio diventa Cold Start da zero. Ciò accade su dispositivi con 2–3 GB di RAM con più app in esecuzione simultanea. In effetti, Warm Start è garantito solo per 10–20 minuti dopo la minimizzazione su dispositivi di fascia media.

Il ViewModel viene preservato durante Warm Start?

Sì, se il processo non è stato ucciso, il ViewModel rimane in memoria e onCleared non viene chiamato. Questo è un vantaggio chiave di Warm Start: tutti i dati caricati tramite richieste di rete, la cache in ViewModel — tutto immediatamente disponibile. Se il processo è stato ucciso, il ViewModel viene ricreato tramite ViewModelProvider.Factory o @HiltViewModel e SavedStateHandle ripristina i campi salvati.

Perché Warm Start può essere più lento di Cold Start?

Teoricamente, Warm Start è sempre più veloce di Cold Start, ma in pratica ci sono scenari in cui la differenza è minima: se Application.onCreate era leggero (50 ms) e Activity.onCreate è pesante (800 ms), allora Warm Start (800 ms) è quasi uguale a Cold Start (850 ms). In questo caso, dovresti ottimizzare non Application, ma Activity.onCreate — diventa il collo di bottiglia per Warm Start.

Come influisce l'API SplashScreen su Warm Start?

L'API SplashScreen su Android 12+ mostra un splash di sistema (icona su sfondo colorato) immediatamente all'avvio — sia per Cold che per Warm Start. Per Warm Start, lo splash viene mostrato solo per 100–300 ms, dopodiché viene sostituito dall'UI dell'app. SplashScreen non accelera l'avvio in sé, ma maschera il tempo di creazione dell'Activity, migliorando la percezione.

È necessario ottimizzare Warm Start se Cold Start è già veloce?

Sì, perché Warm Start si verifica 2–3 volte più spesso di Cold Start. Se Cold Start richiede 1.2 secondi e Warm Start 600 ms, allora il 40% degli avvii (Warm) richiede ancora 0.6 secondi, che è percepibile. Ottimizzare Warm Start a 200–300 ms dà all'utente una sensazione di ritorno istantaneo. Su dispositivi con 6+ GB di RAM, Warm Start può rappresentare fino all'80% di tutti gli avvii, rendendo la sua ottimizzazione una priorità.

Riepilogo

  • Warm Start — avvio dell'app con processo esistente, senza Activity in memoria, tempo 200–800 ms
  • Differenza principale da Cold Start: Application.onCreate non viene eseguito, classi caricate
  • Tre fasi di Warm Start: finestra di avvio → creazione dell'Activity → primo fotogramma
  • Misurato tramite ADB senza flag -S o Macrobenchmark con StartupMode.WARM
  • Ottimizzazione: SavedStateHandle, AsyncLayoutInflater, cache-then-network, ConstraintLayout
  • ViewModel preservato durante Warm Start (processo vivo) — dati immediatamente disponibili
  • Warm Start rappresenta il 40–80% di tutti gli avvii delle app

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche