Punti chiave
App Lifecycle è una macchina a stati finiti che descrive tutti gli stati possibili di un'applicazione mobile e le transizioni tra di essi. Ogni stato determina se l'app può eseguire codice, visualizzare l'interfaccia utente e consumare risorse. iOS e Android hanno modelli simili ma non identici — le differenze derivano dall'architettura del sistema operativo: iOS utilizza un controllo rigoroso della memoria (stato suspended), mentre Android utilizza una gestione flessibile dei processi attraverso onSaveInstanceState.
In iOS, il ciclo di vita è strettamente legato al primo piano/sfondo: l'app è attiva o sospesa. In Android, il ciclo di vita è più granulare — una Activity/Finestra attraversa 6+ stati, e il processo stesso ha un ciclo di vita aggiuntivo attraverso ProcessLifecycleOwner. Le applicazioni moderne (dal 2024) utilizzano sempre più pattern architetturali basati sul ciclo di vita: SwiftUI Lifecycle (iOS 16+) attraverso l'ambiente .scenePhase, Compose Lifecycle attraverso LifecycleEventObserver. Questi pattern gestiscono automaticamente il cambio di contesto e prevengono le perdite di memoria. Secondo Google, la corretta gestione del ciclo di vita in Android riduce il consumo di memoria del 30–40 % durante il lavoro in background.
In iOS, il ciclo di vita dell'app è gestito attraverso la classe UIApplicationDelegate (AppDelegate). I metodi principali: application(_:didFinishLaunchingWithOptions:) — inizializzazione al primo avvio; applicationDidBecomeActive — l'app è visibile e pronta all'interazione; applicationWillResignActive — transizione allo stato inattivo (chiamata in arrivo, chiusura del pannello notifiche); applicationDidEnterBackground — l'app è nascosta, ~5 secondi per salvare i dati; applicationWillEnterForeground — ritorno dallo sfondo; applicationWillTerminate — terminazione (chiamato solo per app meno recenti senza suspended).
A partire da iOS 13, Apple ha introdotto SceneDelegate (UISceneDelegate) per supportare applicazioni multi-finestra su iPad e macOS Catalyst. SceneDelegate gestisce il ciclo di vita di ogni singola scena (finestra): scene(_:willConnectTo:options:) — creazione della scena; sceneDidBecomeActive — scena attiva; sceneWillResignActive — scena perde il focus; sceneDidEnterBackground — scena nascosta. AppDelegate gestisce gli eventi globali (caricamento dati, notifiche push), mentre SceneDelegate gestisce lo stato dell'interfaccia utente di ogni finestra. In SwiftUI, il ciclo di vita è gestito attraverso il valore ambientale @Environment(\.scenePhase): .active, .inactive, .background — questo fornisce un'API unificata per applicazioni SwiftUI e UIKit.
In Android, il ciclo di vita inizia con la classe Application — un singleton creato prima di qualsiasi Activity. Il metodo Application.onCreate() è il primo punto di ingresso, utilizzato per l'inizializzazione globale (Analytics, DI, Crash Reporting). Successivamente, l'Activity viene avviata con la sequenza: onCreate() → onStart() → onResume(). Durante la minimizzazione: onPause() → onStop() → onSaveInstanceState(). Durante la distruzione: onDestroy(). Al ritorno: onRestart() → onStart() → onResume().
ProcessLifecycleOwner (da AndroidX Lifecycle 2.2+) traccia il ciclo di vita dell'intero processo, non di una singola Activity. Fornisce due stati: ON_RESUME (app in primo piano) e ON_STOP (app nascosta). ProcessLifecycleOwner risolve il problema di «quando l'app è andata in background» a livello di processo — questo è fondamentale per SDK, librerie e applicazioni modulari. In Jetpack Compose, il ciclo di vita è gestito attraverso LifecycleEventObserver e collectAsStateWithLifecycle, che sospende automaticamente la raccolta dati in modalità background. Android 14 (API 34) ha aggiunto la notifica del ciclo di vita del servizio in primo piano tramite Service.onTimeout — il servizio deve completarsi entro 6 ore, altrimenti il sistema lo ferma forzatamente.
| iOS | Android | Descrizione |
|---|---|---|
| Not Running | — | L'app non è stata avviata |
| didFinishLaunching | Application.onCreate | Prima inizializzazione all'avvio |
| Active | onResume | L'app è visibile e accetta input |
| Inactive | onPause | Perdita temporanea del focus (chiamata, pannello notifiche) |
| Background | onStop | L'app è nascosta, il codice può essere eseguito |
| Suspended | — | Il codice non viene eseguito, la memoria è riservata |
| Will Terminate | onDestroy | L'app sta terminando |
La differenza principale: iOS forza il passaggio dell'app a Suspended (il codice smette di eseguirsi) 5-10 secondi dopo l'ingresso in background. Android consente il lavoro in background (servizi, WorkManager) per un periodo più lungo, ma con le limitazioni della Modalità Doze (Android 6+) e delle restrizioni in background (Android 12+). ProcessLifecycleOwner in Android è l'equivalente di UIApplicationDidEnterBackgroundNotification in iOS, con la differenza che si attiva su qualsiasi transizione in background, non solo dopo la perdita del focus.
Implementazione di base di AppDelegate che gestisce tutti gli stati del ciclo di vita. Salvataggio dei dati durante la transizione in background e aggiornamento dell'interfaccia utente al ritorno.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegate è il punto centrale del ciclo di vita. applicationDidEnterBackground è un metodo critico: iOS dà ~5 secondi per salvare. applicationDidBecomeActive — aggiornare l'interfaccia utente, riavviare le animazioni. Per SceneDelegate (iOS 13+), la logica del ciclo di vita è distribuita tra le scene: sceneDidBecomeActive / sceneDidEnterBackground gestiscono finestre individuali.
SceneDelegate per applicazioni iPad multi-finestra. Ogni scena ha il proprio ciclo di vita, indipendente dalle altre finestre.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegate gestisce il ciclo di vita di una singola scena. sceneDidBecomeActive / sceneDidEnterBackground sono gli equivalenti dei metodi di AppDelegate per una finestra specifica. sceneDidDisconnect viene chiamato quando una scena viene chiusa (l'utente ha chiuso una finestra su iPad). Per la compatibilità con iOS 12 e versioni precedenti, AppDelegate deve duplicare la logica di SceneDelegate. In iOS 17+, puoi usare @Observable per sospendere/riprendere automaticamente le attività in base a scenePhase.
ProcessLifecycleOwner traccia quando l'app è in primo piano/sfondo a livello di processo. Questo è l'approccio ottimale per la gestione globale del ciclo di vita.
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwner è l'unico modo corretto per tracciare la transizione in background a livello di applicazione. ON_START si attiva quando l'app diventa visibile (primo piano), ON_STOP — quando è completamente nascosta (sfondo). A differenza del ciclo di vita di Activity, ProcessLifecycleOwner non è legato a uno schermo specifico. Per Jetpack Compose, utilizza LifecycleResumeEffect o collectAsStateWithLifecycle invece dell'osservazione manuale.
ViewModel + Lifecycle è l'architettura Android corretta per la gestione del ciclo di vita. ViewModel viene automaticamente pulito in onDestroy, prevenendo perdite.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — rilascio risorse")
}
}
// In Activity
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — sospensione animazioni")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — pulizia")
}
}
ViewModel sopravvive alla rotazione dello schermo e viene distrutto solo al termine dell'Activity. onCleared() è il luogo per rilasciare risorse e cancellare le sottoscrizioni ai flussi. LiveData sospende automaticamente l'osservazione in onPause e la riprende in onResume — questo fornisce protezione contro perdite e crash durante il lavoro in background. In Compose, usa collectAsStateWithLifecycle() per lo stesso scopo.
Domande frequenti
Durante la minimizzazione, l'app passa da attivo a inattivo (brevemente), poi in background. Dopo alcuni secondi, il sistema può spostarla in suspended — il codice smette di eseguirsi, la memoria è riservata. Quando la memoria è insufficiente, il sistema termina l'app sospesa (willTerminate non viene chiamato). Salva i tuoi dati in applicationDidEnterBackground — questo è l'ultimo punto garantito per il salvataggio.
Minimo obbligatorio: applicationDidEnterBackground — salvare i dati utente; applicationWillEnterForeground — aggiornare l'interfaccia utente; in Android onPause — sospendere animazioni/sensori. ProcessLifecycleOwner semplifica la gestione: onResume — onPause per il primo piano, onStart — onStop per la visibilità. Gestisci gli altri stati secondo necessità — non tutti gli stati sono critici per ogni applicazione.
AppDelegate è il delegato globale dell'intera applicazione (avvio, transizione in background). SceneDelegate (iOS 13+) gestisce il ciclo di vita di una finestra individuale (scena) — nelle applicazioni iPad multi-finestra, ogni scena ha il proprio ciclo di vita. Per iOS 12 e versioni precedenti, tutto il ciclo di vita è gestito tramite AppDelegate. Con iPadOS 16+, SceneDelegate è obbligatorio per Stage Manager.
ProcessLifecycleOwner è un componente di AndroidX Lifecycle che traccia il ciclo di vita dell'intera applicazione (processo), non di una singola Activity. Fornisce due eventi principali: ON_RESUME (app in primo piano) e ON_STOP (app in background). Viene utilizzato per l'inizializzazione/pulizia di SDK, analisi, monitor di rete — tutto ciò che deve reagire al passaggio dell'app in background, indipendentemente dallo schermo corrente.
In Jetpack Compose, utilizza LifecycleEventObserver tramite LocalLifecycleOwner.current.lifecycle o la funzione collectAsStateWithLifecycle(). Per gli schermi Compose, utilizza LifecycleResumeEffect — simile a DisposableEffect, che viene eseguito in onResume e pulito in onPause. lifecycleScope.launchWhenResumed — una coroutine che si sospende automaticamente in background.
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