lifecycleScope è un CoroutineScope integrato della libreria androidx.lifecycle, legato al ciclo di vita di un’Activity, di un Fragment o di qualsiasi LifecycleOwner, e annulla automaticamente le coroutine quando il componente viene distrutto. Secondo Google Android Developers, 2025, lifecycleScope consente di eseguire in sicurezza le coroutine legate al livello UI senza il rischio di eseguire codice dopo la distruzione dell’Activity o del Fragment. Lo scope viene automaticamente annullato quando il LifecycleOwner passa allo stato DESTROYED.
Punti chiave
lifecycleScope è una proprietà di estensione sull’interfaccia LifecycleOwner (Activity, Fragment, Service) che fornisce un CoroutineScope pronto all’uso, legato al ciclo di vita completo del componente. Quando il LifecycleOwner raggiunge lo stato DESTROYED, lifecycleScope annulla automaticamente tutte le coroutine attive.
// In Fragment or Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("Ciao!")
}
A differenza di viewModelScope, lifecycleScope viene annullato ogni volta che il LifecycleOwner viene distrutto — inclusa la rotazione dello schermo. Questo lo rende ideale per operazioni che dovrebbero vivere solo finché un determinato schermo è visibile.
lifecycleScope è disponibile ovunque ci sia un LifecycleOwner:
Il meccanismo di cancellazione automatica di lifecycleScope si basa sull’iscrizione agli eventi Lifecycle. Quando il Lifecycle scende al di sotto di CREATED verso DESTROYED, lo scope viene annullato.
| Stato | Descrizione | Scope attivo |
|---|---|---|
| CREATED | LifecycleOwner creato, onCreate eseguito | Sì |
| STARTED | LifecycleOwner visibile (onStart) | Sì |
| RESUMED | LifecycleOwner in primo piano (onResume) | Sì |
| DESTROYED | LifecycleOwner distrutto (onDestroy) | No (scope annullato) |
lifecycleScope viene creato come CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) e memorizzato all’interno del Lifecycle. Quando il Lifecycle passa allo stato DESTROYED, viene chiamato scope.cancel(). Il meccanismo è implementato tramite LifecycleEventObserver, che si iscrive agli eventi del ciclo di vita al primo accesso allo scope.
Quando lo schermo viene ruotato, l’Activity viene distrutta (onDestroy) e ricreata. lifecycleScope viene annullato insieme alla vecchia Activity e viene creata una nuova istanza dello scope per la nuova Activity. Questa è una differenza fondamentale rispetto a viewModelScope, che sopravvive alla rotazione.
La libreria lifecycle fornisce diversi modi per avviare coroutine tramite lifecycleScope. Esaminiamo l’evoluzione dell’API dai metodi obsoleti a quelli moderni.
Il modo più semplice è lifecycleScope.launch { ... }. La coroutine si avvia immediatamente e viene annullata a DESTROYED. Tuttavia, può eseguire codice anche quando l’UI non è visibile (ad esempio, in background dopo onStop). Ciò non è sempre desiderabile.
Questi metodi mettevano in pausa l’esecuzione della coroutine quando il Lifecycle scendeva al di sotto dello stato specificato e la riprendevano al ritorno. Tuttavia, sono stati contrassegnati come @Deprecated in lifecycle-runtime-ktx 2.6.0 perché:
repeatOnLifecycle è il metodo consigliato da Google per avviare coroutine sincronizzate con il ciclo di vita. Annulla e riavvia la coroutine ogni volta che il Lifecycle raggiunge lo stato specificato.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
La coroutine passata a repeatOnLifecycle si avvia quando il Lifecycle raggiunge STARTED e viene annullata quando scende al di sotto di STARTED. Al ritorno a STARTED, la coroutine si riavvia da capo. Questo è sicuro ed efficiente — nessuna coroutine rimane in sospeso.
Per raccogliere dati da Flow con consapevolezza del ciclo di vita, esiste l’operatore flowWithLifecycle. Arresta e riprende automaticamente la raccolta quando lo stato del Lifecycle cambia:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
L’operatore flowWithLifecycle è il modo più conciso per abbonarsi in sicurezza a un Flow nel livello UI.
Esaminiamo tre scenari reali di utilizzo di lifecycleScope in un’applicazione Android con Kotlin.
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
La coroutine si avvia quando il fragment diventa visibile (STARTED) e viene annullata quando esce dallo schermo (STOPPED). Se l’utente passa a un’altra applicazione, gli aggiornamenti di posizione non consumano batteria.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
L’animazione viene eseguita solo quando il fragment è in primo piano (RESUMED). Se l’utente minimizza l’app durante l’animazione, la coroutine viene annullata e, al ritorno, l’animazione si riavvia.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
I dati vengono sincronizzati ogni 30 secondi, ma solo quando lo schermo è visibile. isActive verifica se la coroutine è stata annullata, fornendo un’uscita sicura dal ciclo quando si lascia lo schermo.
Entrambi gli scope sono legati al ciclo di vita, ma a diversi aspetti di esso. Comprendere la differenza è fondamentale per un’architettura corretta delle applicazioni Android.
viewModelScope è legato al ViewModel, che sopravvive alla rotazione dello schermo. lifecycleScope è legato al LifecycleOwner (Activity/Fragment), che viene distrutto e ricreato durante la rotazione. Questo determina i loro scenari d’uso.
In pratica, è comune una combinazione di entrambi gli scope: viewModelScope carica i dati e gestisce lo stato, mentre lifecycleScope si iscrive al Flow dal ViewModel con consapevolezza del ciclo di vita. Questa separazione delle responsabilità è considerata una best practice nello sviluppo Android moderno.
Esaminiamo quattro degli errori più comuni commessi dagli sviluppatori nell’utilizzo di lifecycleScope.
Se si avvia il caricamento dati in lifecycleScope.launch, la coroutine verrà annullata durante la rotazione dello schermo e i dati dovranno essere ricaricati. Usa viewModelScope per le operazioni a lunga durata. lifecycleScope è solo per le attività legate all’UI.
Chiamare direttamente viewModel.someFlow.collect { ... } all’interno di lifecycleScope.launch continua a raccogliere dati anche quando lo schermo non è visibile. Ciò può portare ad aggiornamenti dell’UI in background e un sovraccarico inutile. Usa sempre repeatOnLifecycle o flowWithLifecycle.
Sebbene lifecycleScope venga annullato a DESTROYED, il codice dopo un punto di sospensione potrebbe non essere eseguito in caso di annullamento improvviso. Non fare affidamento sull’esecuzione del codice dopo una chiamata suspend, a meno che tu non stia usando NonCancellable.
launchWhenStarted e i suoi equivalenti non annullano la coroutine, la mettono solo in pausa. Se lo schermo passa tra primo piano e sfondo più volte, la coroutine accumula chiamate differite. Passa a repeatOnLifecycle — questo è l’unico modo corretto per sincronizzarsi con il Lifecycle.
Domande frequenti
lifecycleScope viene automaticamente annullato quando il LifecycleOwner viene distrutto. GlobalScope vive per tutta la durata dell’applicazione. Una coroutine in lifecycleScope non può aggiornare l’UI dopo la distruzione del componente, mentre in GlobalScope può farlo, causando crash. Usa sempre lifecycleScope nel livello UI.
No, ViewModel non è un LifecycleOwner, quindi lifecycleScope non è disponibile al suo interno. ViewModel usa viewModelScope. Se il codice deve essere eseguito in entrambi i contesti, estrai la logica in un use case o repository con funzioni suspend.
Ogni chiamata a repeatOnLifecycle crea una nuova coroutine che esegue il blocco quando viene raggiunto lo stato specificato del Lifecycle. Se repeatOnLifecycle viene chiamato due volte per lo stesso stato, entrambi i blocchi verranno eseguiti indipendentemente. Di solito, una singola chiamata in onViewCreated è sufficiente.
Non è possibile modificare direttamente il dispatcher di lifecycleScope — usa Dispatchers.Main.immediate. All’interno del blocco della coroutine, puoi passare a un altro dispatcher tramite withContext. Per i test, usa TestDispatcher con LifecycleOwner.
lifecycleScope viene annullato quando il LifecycleOwner passa allo stato DESTROYED (dopo onDestroy). Le semplici chiamate a lifecycleScope.launch non vengono annullate in onPause o onStop. Per mettere in pausa quando si passa in background, usa repeatOnLifecycle(STARTED) o repeatOnLifecycle(RESUMED).
Riepilogo
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.
Leggi anche