onPause: cos'è, salvare lo stato di Activity in Android

Autore: IT Sectr Pubblicato: 2026-03-04 Tempo di lettura: 10 min

onPause è un metodo del ciclo di vita di Android che viene chiamato quando un Activity perde il focus di input ma rimane parzialmente visibile sullo schermo. Il sistema chiama onPause prima che un nuovo Activity arrivi in primo piano, all'apertura di una finestra di dialogo, quando si preme il pulsante App recenti o quando arriva una chiamata in entrata. Questo metodo è l'ultimo punto garantito per salvare i dati utente, poiché dopo onStop e onDestroy il sistema può terminare il processo senza chiamate aggiuntive. All'interno di onPause, lo sviluppatore salva bozze, mette in pausa animazioni, rilascia la fotocamera e scrive lo stato corrente dell'UI in SharedPreferences. Per maggiori dettagli sul ciclo di vita completo di Activity, leggi l'articolo Activity Lifecycle.

Punti Chiave

  • onPause — Activity perde il focus ma rimane visibile; ultimo punto garantito per salvare i dati
  • Salvare Stato — in onPause vengono salvati i dati critici dell'utente: bozze, testo nei moduli, progressi
  • Rilasciare Risorse — fotocamera, microfono, lettore video vengono rilasciati in onPause per il trasferimento a un'altra app
  • Limite di Tempo — onPause deve completarsi entro 100 ms; il superamento causa ANR e ritarda la transizione
  • SharedPreferences.apply() — scrittura asincrona in onPause; commit() blocca il thread e può causare ANR
  • onPause vs onStop — onPause con visibilità parziale (dialogo), onStop con occultamento completo (altro Activity)
  • onSaveInstanceState — chiamato dopo onPause per salvare lo stato temporaneo in un Bundle

Cos'è onPause in Android

onPause è il quarto metodo del ciclo di vita di Activity, chiamato quando lo schermo perde il focus di input ma rimane parzialmente visibile all'utente. È uno stato di «transizione» tra l'esecuzione attiva dell'app e il suo occultamento. Il sistema chiama onPause nei seguenti scenari: apertura di un altro Activity (un nuovo schermo copre parzialmente quello corrente), comparsa di una finestra di dialogo (Dialog, PopupWindow, Snackbar non attivano onPause, ma DialogFragment lo fa), pressione del pulsante App recenti, chiamata in entrata, pressione del pulsante di accensione per bloccare lo schermo.

Il compito principale di onPause è preparare l'app alla possibilità di essere nascosta o distrutta. Questo è l'ultimo punto nel ciclo di vita in cui lo sviluppatore può essere certo che il suo codice verrà eseguito prima che il sistema proceda con la transizione a un altro componente. Dopo onPause, il sistema chiama onStop (se l'Activity è completamente nascosta), dopodiché il processo può essere terminato in qualsiasi momento senza ulteriore notifica.

Secondo la documentazione di Android Developers (2025), onPause dovrebbe essere il più leggero e veloce possibile. Finché onPause non restituisce il controllo, il sistema non può avviare il successivo Activity — significa che l'utente vede un ritardo nella transizione dello schermo. Google raccomanda di completare onPause in meno di 100 millisecondi e tutte le operazioni lunghe (salvataggio nel database, scrittura su disco) dovrebbero essere eseguite in modo asincrono tramite coroutine o apply().

onPause in Activity

In un Activity, il metodo onPause viene chiamato ogni volta che lo schermo cessa di essere attivo ma può continuare a essere visualizzato parzialmente. Un esempio tipico: l'utente apre l'app Mappe, tocca Condividi posizione e viene visualizzata una finestra di dialogo di sistema per la selezione dell'app sopra le Mappe. L'Activity delle Mappe riceve onPause ma rimane visibile sotto il dialogo. Quando il dialogo viene chiuso, le Mappe ricevono onResume senza che venga chiamato onStart (lo schermo non è stato completamente nascosto).

kotlin
class NoteEditorActivity : AppCompatActivity() {
    private var binding: ActivityNoteEditorBinding? = null
    private val prefs by lazy {
        getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
    }

    override fun onPause() {
        super.onPause()

        // Salva bozza della nota — in modo asincrono
        prefs.edit()
            .putString("draft_title", binding?.titleInput?.text.toString())
            .putString("draft_body", binding?.bodyInput?.text.toString())
            .putLong("draft_timestamp", System.currentTimeMillis())
            .apply()

        // Metti in pausa il video
        binding?.videoPlayer?.pause()

        // Rilascia risorse esclusive
        releaseCamera()
        releaseAudioFocus()
    }

    override fun onResume() {
        super.onResume()
        // Ripristina bozza
        binding?.titleInput?.setText(prefs.getString("draft_title", ""))
        binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
        acquireCamera()
        acquireAudioFocus()
    }
}

L'esempio NoteEditorActivity dimostra la corretta gestione di onPause: salvare una bozza in SharedPreferences tramite apply(), mettere in pausa un file video, rilasciare la fotocamera e il focus audio. Ogni chiamata è leggera e veloce, senza bloccare il thread UI abbastanza a lungo da causare ANR. Notare l'ordine: super.onPause() viene chiamato sulla prima riga — questo garantisce che la logica di sistema venga eseguita anche in caso di eccezione nel codice utente.

Salvare Stato in onPause

onPause è l'ultimo punto in cui lo sviluppatore può salvare in modo affidabile i dati utente prima che l'app venga nascosta o uccisa dal sistema. Dopo onStop, il sistema può terminare il processo se la memoria è scarsa, senza chiamare onDestroy. Il metodo onSaveInstanceState() viene chiamato dopo onPause, ma il suo Bundle non è destinato all'archiviazione a lungo termine — vive solo fino al prossimo onCreate.

SharedPreferences con apply()

SharedPreferences con apply() asincrono è il modo ottimale per salvare piccole quantità di dati in onPause. A differenza di commit(), che scrive i dati in modo sincrono su disco e restituisce un booleano, apply() salva immediatamente i dati in memoria e pianifica una scrittura asincrona su disco. Questo richiede meno di 1 millisecondo sul thread UI contro i 10–100 millisecondi di commit().

kotlin
override fun onPause() {
    super.onPause()

    // ❌ Male: la scrittura sincrona blocca il thread
    // prefs.edit().putInt("score", score).commit()

    // ✅ Bene: scrittura asincrona
    prefs.edit().putInt("score", score).apply()

    // Per oggetti complessi — memorizzazione nella cache in ViewModel
    viewModel.saveState()
}

Room e Coroutine

Per dati strutturati (SQLite tramite Room) in onPause, si usano coroutine con lifecycleScope. ViewModelScope annulla automaticamente la coroutine quando il ViewModel viene distrutto, prevenendo scritture in un database chiuso. La scrittura tramite Room con coroutine richiede 5–15 millisecondi e non blocca il thread UI.

kotlin
// In ViewModel:
fun saveDraft(title: String, body: String) {
    viewModelScope.launch(Dispatchers.IO) {
        noteDao.insert(NoteDraft(title = title, body = body))
    }
}

// In Activity.onPause:
viewModel.saveDraft(
    binding?.titleInput?.text.toString(),
    binding?.bodyInput?.text.toString()
)

onPause in Fragment

onPause in Fragment viene chiamato quando il Fragment cessa di essere attivo ma può rimanere visibile. Ciò accade quando: un Fragment viene sostituito da un altro Fragment tramite FragmentTransaction; un Fragment non è più la pagina corrente in un ViewPager; l'Activity che contiene il Fragment riceve onPause. L'interazione tra onPause di Activity e onPause di Fragment è strettamente gerarchica: prima l'Activity riceve onPause, poi tutti i suoi Fragment.

kotlin
class MapFragment : Fragment() {
    private var mapController: MapController? = null

    override fun onPause() {
        super.onPause()
        mapController?.stopFollowMode()
        binding?.mapContainer?.alpha = 0.7f
    }

    override fun onResume() {
        super.onResume()
        binding?.mapContainer?.alpha = 1.0f
        if (isVisible) {
            mapController?.startFollowMode()
        }
    }
}

Specificità del lavoro con le mappe in onPause: Google Maps e Yandex Maps consumano risorse GPU significative in modalità di inseguimento attivo. Quando si perde il focus, ha senso disabilitare l'animazione della mappa e ridurre la frequenza di aggiornamento dei marker, e quando il focus viene recuperato, ripristinare la piena funzionalità. Questo migliora le prestazioni e riduce il consumo energetico durante il passaggio tra schermate.

onPause vs onStop: Differenza e Scenari

Una delle confusioni più comuni tra gli sviluppatori Android principianti è non comprendere la differenza tra onPause e onStop. Esaminiamo ogni scenario e determiniamo il metodo corretto.

ScenarioonPauseonStop
Apertura di una finestra di dialogoChiamatoNon chiamato
Apertura di un nuovo Activity (non trasparente)ChiamatoChiamato
Pressione del pulsante HomeChiamatoChiamato
Blocco schermoChiamatoChiamato
Chiamata in entrataChiamatoChiamato
Activity trasparente sovrappostoChiamatoNon chiamato
Split Screen (metà schermo)ChiamatoNon chiamato
PiP (Picture-in-Picture)ChiamatoNon chiamato

La regola principale: onPause viene chiamato a qualsiasi perdita di focus, onStop viene chiamato solo quando la visibilità è completamente persa. Se l'Activity rimane visibile (anche parzialmente), onStop non viene chiamato. Questo è criticamente importante per le modalità Split Screen, PiP e Activity trasparente — qui onPause/onResume funzionano, ma onStart/onStop no.

Tempi e Prestazioni di onPause

onPause è il metodo del ciclo di vita più critico in termini di tempo, perché blocca il rendering del successivo Activity. Il sistema attende il completamento di onPause dell'Activity corrente prima di mostrare quello nuovo. Se onPause richiede più di 100 millisecondi, l'utente nota un ritardo nella transizione; se più di 5 secondi, il sistema mostra un ANR.

Raccomandazioni sulle Prestazioni

La Guida alle Prestazioni Android di Google (2025) offre le seguenti raccomandazioni per onPause: non eseguire richieste di rete — vanno cancellate o spostate in WorkManager; non scrivere file grandi su disco — usa BufferedWriter in un thread in background; non eseguire query SQL complesse — le operazioni Room devono essere asincrone tramite coroutine; evita di creare nuovi oggetti — la garbage collection in onPause aggrava il ritardo; usa apply() invece di commit() per SharedPreferences.

kotlin
override fun onPause() {
    super.onPause()

    // ❌ Male: la richiesta HTTP blocca l'UI
    // val response = api.syncSave(data).execute()

    // ❌ Male: scrittura sincrona in file
    // FileOutputStream(file).write(data)

    // ✅ Bene: salvataggio asincrono
    lifecycleScope.launch {
        withContext(Dispatchers.IO) {
            api.saveData(data)
            fileDao.write(data)
        }
    }

    // ✅ Bene: scrittura leggera in SharedPreferences
    prefs.edit().putString("key", value).apply()
}

La profilazione di onPause tramite Android Studio Profiler (traccia CPU) mostra il tempo di esecuzione esatto. Se onPause impiega più di 100 ms, Profiler evidenzia il metodo in giallo, e più di 500 ms in rosso. Nei progetti commerciali di IT Sectr, utilizziamo test Macrobenchmark che verificano automaticamente il tempo di transizione tra Activity e segnalano regressioni di prestazioni nella pipeline CI.

Errori Comuni in onPause

Anche gli sviluppatori esperti commettono errori in onPause. Esaminiamo cinque problemi tipici e le loro soluzioni.

Scrittura Sincrona nel Database

Chiamare Room DAO con una query sincrona (.executeAsObservable() senza coroutine) in onPause blocca il thread UI per 10–50 ms. Se nello stesso momento si verifica GC o contesa di scrittura, il ritardo può raggiungere 200–500 ms. Soluzione: usa coroutine con Dispatchers.IO o apply() per SharedPreferences.

Registrare Nuovi Listener

onPause non è il posto per registrare listener. Se registri un BroadcastReceiver in onPause, rimarrà attivo quando l'Activity non è più visibile. La registrazione dovrebbe avvenire solo in onStart/onResume, e in onPause/onStop — solo la cancellazione della registrazione. L'eccezione sono le API basate su Intent che richiedono la registrazione prima della chiamata.

Ignorare le Eccezioni

Se si verifica un'eccezione non gestita in onPause, il sistema non chiama onStop e onDestroy. L'Activity si blocca in uno stato indefinito e onResume al ritorno potrebbe non ripristinare correttamente le risorse rilasciate. Soluzione: avvolgi le operazioni critiche in try/catch con registrazione tramite Log.e().

Salvare Dati Ridondanti

Non è necessario salvare in onPause dati che possono essere facilmente ripristinati. Ad esempio, i risultati delle richieste API vengono memorizzati nella cache in Room o DataStore al momento del recupero, non in onPause. Salva solo ciò che l'utente ha inserito manualmente e non può essere ripristinato automaticamente — testo nei campi, elementi selezionati, posizione di scorrimento.

Dimenticato super.onPause()

super.onPause() dovrebbe essere chiamato, ma a differenza di onCreate, ometterlo non causa un crash immediato. Il sistema «perdona» l'assenza di super in onPause, ma la macchina a stati interna entra in uno stato errato. La successiva chiamata onResume potrebbe non ripristinare il focus di input, lasciando l'Activity «congelata». Chiama sempre super.onPause() il prima possibile.

Domande Frequenti

Cosa succede se si chiama finish() in onPause?

Chiamare finish() in onPause terminerà l'Activity immediatamente dopo il ritorno dal metodo. Questo è uno scenario valido se lo schermo deve essere chiuso quando si perde il focus (ad esempio, uno schermo di autorizzazione quando si minimizza l'app). Tuttavia, finish() attiva il ciclo completo di terminazione: onStop onDestroy, che aggiunge un ritardo alla transizione. Usa finish() in onPause solo quando è veramente necessario.

In cosa onPause si differenzia da onSaveInstanceState?

onPause serve per salvare dati che devono sopravvivere alla terminazione del processo (bozze in SharedPreferences/Room). onSaveInstanceState serve per salvare lo stato temporaneo dell'UI necessario solo fino al prossimo onCreate (posizione di scorrimento, scheda selezionata). Il Bundle di onSaveInstanceState non viene preservato quando l'app viene terminata completamente — esiste solo in memoria. I dati di onPause vengono salvati su disco e sopravvivono a un riavvio.

È possibile aprire un dialogo in onPause?

Non raccomandato. Aprire un dialogo o popup in onPause causa una WindowLeakException se l'Activity è già stata terminata. Se devi mostrare una notifica quando si perde il focus, usa NotificationManager (notifiche di sistema) — è sicuro e previsto dall'utente. Per azioni differite, usa AlarmManager o WorkManager.

Perché onPause è un punto di salvataggio garantito, ma onStop no?

onPause è garantito essere chiamato prima che l'Activity cessi di essere attiva. onStop potrebbe non essere chiamato se il sistema uccide il processo per liberare memoria — in questo caso, anche onDestroy non viene chiamato. onPause è l'unico metodo dopo onResume che viene sempre chiamato, indipendentemente dal motivo della perdita di focus. Pertanto, tutti i dati critici vengono salvati proprio in onPause.

Come testare onPause nei test unitari?

Per testare onPause si usano Robolectric o FragmentScenario di AndroidX Test. FragmentScenario.create() moveToState(State.STARTED) moveToState(State.RESUMED) moveToState(State.STARTED) chiama onPause sequenzialmente. Poi si verifica che i dati siano stati salvati in SharedPreferences o che la fotocamera sia stata rilasciata tramite un oggetto mock. Robolectric 4.12+ supporta l'emulazione di onPause/onResume senza un dispositivo fisico.

Riepilogo

  • onPause — Activity perde il focus di input ma rimane parzialmente visibile; ultimo punto garantito per salvare i dati
  • Salvataggio — SharedPreferences.apply() o Room tramite coroutine; commit() e operazioni sincrone sono vietati
  • Rilascio Risorse — fotocamera, focus audio, lettore video vengono rilasciati in onPause per il trasferimento a un'altra app
  • Limite 100 ms — onPause blocca il rendering del successivo Activity; il superamento del limite causa ANR
  • onPause vs onStop — onPause alla perdita di focus (visibilità preservata), onStop all'occultamento completo
  • Fragment.onPause — chiamata gerarchica dopo Activity.onPause; specificità per mappe e ViewPager
  • Errori Comuni — scrittura sincrona, registrazione di listener, ignorare try/catch, salvataggio ridondante
  • super.onPause() — chiamare il prima possibile; ometterlo non causa crash ma rompe la macchina a stati

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