Not Running — cos’è, lo stato iniziale del ciclo di vita

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

Not Running — lo stato iniziale del ciclo di vita di un’applicazione mobile quando non è ancora stata avviata o ha già terminato il suo lavoro. Scopri come iOS e Android gestiscono questo stato, quali eventi portano alla transizione da Not Running e come gestire correttamente l’avvio e la terminazione dell’applicazione in Swift e Kotlin.

Punti chiave

  • Not Running — l’applicazione non è caricata in memoria e non esegue codice; è il punto di ingresso e uscita del ciclo di vita
  • Avvio — la transizione da Not Running avviene toccando l’icona dell’app, tramite deep link o notifica push
  • Terminazione — l’utente chiude l’app con uno swipe, il sistema la scarica per mancanza di memoria o si verifica un crash
  • Avvio a freddo — l’applicazione parte da zero, tutti gli oggetti vengono creati di nuovo, lo stato non viene ripristinato dalla cache
  • Avvio a caldo — l’applicazione era in Suspended e torna in Active senza inizializzazione completa

Not Running — cos’è questo stato

Not Running è lo stato di base del ciclo di vita di un’applicazione mobile in cui non è caricata nella RAM del dispositivo e non consuma risorse di sistema. In iOS e Android, questo stato significa la completa assenza di processi e thread associati all’applicazione. L’utente vede l’icona dell’app sulla schermata home, ma l’applicazione stessa non è attiva e non si trova nell’elenco delle app recenti.

Quando l’utente tocca l’icona dell’app, il sistema crea un nuovo processo, carica il codice eseguibile in memoria e inizializza tutte le strutture dati necessarie. Questo processo si chiama avvio a freddo (cold start) ed è il più dispendioso in termini di risorse per quanto riguarda il tempo di caricamento.

Il sistema può spostare l’applicazione in Not Running da qualsiasi altro stato. Se l’applicazione è in background o sospesa, il sistema operativo ha il diritto di scaricarla quando non c’è abbastanza RAM per attività con priorità maggiore — ad esempio, per un’applicazione attiva in primo piano.

Lo sviluppatore deve considerare che l’applicazione può essere terminata dal sistema in qualsiasi momento quando è in background. Ciò significa che tutti i dati non salvati possono essere persi. Pertanto, è di fondamentale importanza salvare lo stato in archivi chiave-valore (UserDefaults, SharedPreferences) o in un database locale durante le transizioni da Active a Background.

Come il sistema determina quale applicazione scaricare

iOS utilizza priorità basate sullo stato corrente dell’applicazione: Active ha la priorità più alta, seguita da Inactive, Background, Suspended e infine Not Running — la priorità più bassa. Android utilizza una gerarchia di processi simile: il processo in primo piano ha priorità OOM_ADJ = 0, processo Visible = 100, processo Service = 200, processo Background = 300, processo Empty = 400. Più alto è il valore, maggiore è la probabilità che il processo venga terminato quando la memoria è scarsa.

PiattaformaStatoPriorità di scaricoDescrizione
iOSNot RunningPiù altaApp non caricata — non consuma risorse di sistema
iOSSuspendedAltaApp in memoria ma codice non eseguito — primo bersaglio per lo scarico
iOSBackgroundMediaApp in esecuzione di attività in background — scaricata dopo timeout
iOSActiveBassaApp attiva — scaricata solo sotto pressione critica di memoria
AndroidEmpty ProcessPiù altaProcesso senza componenti attivi — rimosso per primo
AndroidBackground ProcessAltaProcesso in background senza Activity visibile
AndroidForeground ServiceBassaServizio con notifica — raramente terminato
AndroidForeground ProcessMinimaActivity attiva — terminato per ultimo

Avvio a freddo e a caldo di un’applicazione

Avvio a freddo (cold start) si verifica quando l’applicazione passa direttamente da Not Running ad Active. Il sistema crea un nuovo processo, carica le classi, inizializza i campi statici, crea il thread principale e avvia il framework UI. Su iOS, ciò significa chiamare application(_:didFinishLaunchingWithOptions:), su Android — chiamare Application.onCreate() e Activity.onCreate(). Il tempo di avvio a freddo può variare da 200 ms a diversi secondi a seconda della complessità dell’applicazione.

Avvio a caldo (warm start o hot start) — l’applicazione era nello stato Suspended e riprende senza un ricaricamento completo. Il sistema ripristina l’ultimo stack UI dalla memoria e l’utente continua a lavorare dallo stesso punto. L’avvio a caldo è significativamente più veloce dell’avvio a freddo perché la maggior parte del codice è già caricata in memoria. Su iOS, un avvio a caldo non chiama application(_:didFinishLaunchingWithOptions:), solo applicationWillEnterForeground e applicationDidBecomeActive.

La differenza tra avvio a freddo e a caldo è critica per l’esperienza utente. Durante un avvio a freddo, lo sviluppatore deve garantire che l’avvio avvenga il più rapidamente possibile — inizializzazione pigra dei moduli, caricamento differito delle risorse pesanti, minimizzazione del lavoro nel thread principale all’avvio. Google raccomanda un avvio a freddo non superiore a 500 ms, Apple — non superiore a 400 ms per iOS.

kotlin
// Misurazione del tempo di avvio a freddo in Android
class App : Application() {
    private var startTime: Long = 0L

    override fun onCreate() {
        super.onCreate()
        startTime = System.currentTimeMillis()
    }

    fun getStartupTime(): Long {
        return System.currentTimeMillis() - startTime
    }
}

// Avviare Activity con inizializzazione pigra
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Solo il minimo necessario per il primo frame
        setupNavigation()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        // Inizializzazione pesante dopo il rendering
        initializeHeavyModules()
    }
}

L’esempio mostra la misurazione del tempo di avvio a freddo in Android. Application.onCreate() viene chiamato quando si passa da Not Running ad Active. Il timestamp viene registrato all’avvio del processo. L’Activity utilizza l’inizializzazione pigra tramite un delegato lazy per evitare di bloccare il primo frame. onPostCreate è il luogo ottimale per inizializzare i moduli pesanti poiché l’UI è già stata renderizzata.

Not Running in iOS: Swift e AppDelegate

In iOS, Not Running viene gestito tramite il protocollo UIApplicationDelegate. Metodi chiave: application(_:didFinishLaunchingWithOptions:) viene chiamato dopo un avvio a freddo, applicationWillTerminate(_:) viene chiamato prima che l’utente termini l’applicazione. Tuttavia, il sistema può terminare l’applicazione senza chiamare applicationWillTerminate — ad esempio, durante una terminazione di emergenza o pressione di memoria. iOS non garantisce che questo metodo venga chiamato, quindi i dati devono essere salvati in applicationDidEnterBackground.

Scenari di transizione a Not Running su iOS

L’utente può terminare manualmente l’applicazione con uno swipe nell’App Switcher. Il sistema può scaricare l’applicazione dalla memoria mentre è in background. L’applicazione può crashare. In tutti i casi, tutti gli oggetti creati durante l’avvio vengono distrutti. Lo stato che non è stato salvato viene perso per sempre. In iOS 13+, si consiglia di utilizzare NSUserActivity o il meccanismo di ripristino dello stato tramite UIApplication.stateRestorationIdentifier per preservare lo stato.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // Avvio a freddo: applicazione passata da Not Running
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Inizializzazione del set minimo di servizi
        setupAnalytics()
        configureAppearance()
        return true
    }

    // L’applicazione termina — solo chiusura manuale
    func applicationWillTerminate(
        _ application: UIApplication
    ) {
        saveCriticalData()
    }

    // Salvare i dati prima di andare in background
    func applicationDidEnterBackground(
        _ application: UIApplication
    ) {
        saveApplicationState()
    }

    private func saveCriticalData() {
        UserDefaults.standard.synchronize()
    }

    private func saveApplicationState() {
        let state = ["lastScreen": "main", "timestamp": Date()]
        try? NSKeyedArchiver.archivedData(
            withRootObject: state,
            requiringSecureCoding: true
        )
    }
}

Il codice mostra la corretta gestione di Not Running su iOS. applicationWillTerminate viene chiamato solo quando l’utente termina manualmente l’app. Il salvataggio dei dati critici viene duplicato in applicationDidEnterBackground poiché questo metodo è garantito per essere chiamato prima di andare in background. Il ripristino dello stato consente di salvare lo stack UI per un successivo recupero durante un avvio a freddo.

Not Running in Android: Kotlin e processo

In Android, Not Running significa che il processo dell’applicazione non esiste. Il sistema Linux alla base di Android gestisce i processi attraverso il meccanismo Zygote. Quando un’applicazione viene avviata, Zygote biforca un nuovo processo, carica Dalvik/ART e chiama Application.onCreate(). Android non ha un equivalente diretto di applicationWillTerminate — il sistema può terminare il processo in qualsiasi momento senza preavviso.

Ciclo di vita del processo Android

Quando un’Activity viene chiamata per la prima volta, il sistema crea il processo, l’Application e l’Activity attraverso la catena onCreate → onStart → onResume. Se l’utente preme Indietro, l’Activity viene distrutta (onDestroy) e il processo può essere terminato dal sistema. Una differenza chiave rispetto a iOS: in Android, il processo può continuare a esistere anche senza Activities attive — ad esempio, se è in esecuzione un Foreground Service o c’è un BroadcastReceiver attivo.

kotlin
// Gestione di Not Running tramite SavedStateHandle in ViewModel
class MainViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    companion object {
        private const val KEY_LAST_SCREEN = "last_screen"
        private const val KEY_USER_DATA = "user_data"
    }

    fun saveCurrentState(screen: String, data: String) {
        savedStateHandle[KEY_LAST_SCREEN] = screen
        savedStateHandle[KEY_USER_DATA] = data
    }

    fun restoreState(): AppState? {
        val screen = savedStateHandle.get<String>(KEY_LAST_SCREEN)
        val data = savedStateHandle.get<String>(KEY_USER_DATA)
        return if (screen != null && data != null) {
            AppState(screen, data)
        } else null
    }
}

// Application — il primo callback dopo Not Running
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initCrashReporter()
        initDependencyInjection()
    }
}

SavedStateHandle è un componente di Android Architecture Components che salva automaticamente lo stato durante una transizione a Not Running e lo ripristina in un avvio a freddo. Un ViewModel creato tramite ViewModelProvider sopravvive alla rotazione dello schermo e alla distruzione dell’Activity. Quando il processo termina, i dati da SavedStateHandle vengono serializzati in un Bundle e salvati nello stato dell’istanza salvata.

Motivi della transizione a Not Running

Not Running si verifica per diversi motivi. L’utente chiude manualmente l’applicazione. Il sistema scarica l’applicazione per mancanza di memoria. L’applicazione crasha con un’eccezione. Su Android, il sistema può terminare il processo durante un aggiornamento massivo delle app o un riavvio del dispositivo. iOS può terminare l’applicazione quando un’attività in background va in timeout (di solito 30 secondi).

MotivoiOSAndroidPuò essere prevenuto
Chiusura manuale dall’utenteSwipe in App SwitcherSwipe da RecentsNo — azione dell’utente
Mancanza di memoriaAttivazione avviso di memoriaonTrimMemory / LMKParzialmente — ottimizzazione memoria
Crash dell’applicazioneNSException / segnaleUncaughtException / ANRSì — gestione errori e segnalazione crash
Timeout attività in background30 sec per attività in background10 min per JobSchedulerSì — pianificazione corretta delle attività
Riavvio del sistema operativoapplicationWillTerminate chiamatoBroadcast ACTION_SHUTDOWNNo — evento di sistema
Aggiornamento dell’appNon si verifica (iOS Sandbox)Processo terminato all’aggiornamento APKNo — aggiornamento di sistema

Come diagnosticare una transizione a Not Running

Per iOS, utilizza la registrazione console in applicationWillTerminate e applicationDidFinishLaunching. Aggiungi un flag in UserDefaults ad ogni avvio — se il flag manca al prossimo avvio, l’applicazione è stata terminata in modo improprio. Su Android, usa ActivityManager.isBackgroundRestricted() per verificare se l’applicazione può eseguire attività in background. Inoltre, monitora onTrimMemory(TRIM_MEMORY_COMPLETE) — questo è un segnale che il processo verrà terminato.

Migliori pratiche per lavorare con Not Running

Prima regola — non dare mai per scontato che applicationWillTerminate o onDestroy vengano chiamati. Salva i dati di importanza critica ad ogni transizione da Active a Background. Utilizza archivi chiave-valore per le impostazioni semplici e SQLite/Room per i dati strutturati.

Seconda regola — misura il tempo di avvio a freddo e ottimizzalo. Inizializzazione pigra, minimizzazione del lavoro nel thread principale, precaricamento delle risorse, utilizzo dell’API SplashScreen — tutto ciò migliora la percezione del tempo di avvio. Google raccomanda un avvio a freddo inferiore a 200 ms per un’eccellente esperienza utente.

Terza regola — implementa il ripristino dello stato. Su iOS, usa UIApplication.stateRestorationIdentifier e NSUserActivity. Su Android, usa SavedStateHandle in ViewModel combinato con onSaveInstanceState. Ciò consentirà all’utente di continuare a lavorare dallo stesso punto dopo un riavvio dell’app.

Quarta regola — gestisci launchOptions e Intent con cui l’applicazione è stata avviata dopo Not Running. Deep link, notifiche push, link universali — tutti vengono passati attraverso i parametri di avvio. Lo sviluppatore deve estrarre correttamente questi dati e indirizzare l’utente alla schermata appropriata.

swift
// Gestione del deep link dopo l’avvio a freddo
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Verificare se è arrivata una notifica
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // Verificare il deep link
    if let url = launchOptions?[.url] as? URL {
        handleDeepLink(url)
    }
    return true
}

private func handleDeepLink(_ url: URL) {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
          let screenId = components.queryItems?.first(where: { $0.name == "screen" })?.value
    else { return }
    openScreen(screenId)
}

Il codice mostra la gestione dei parametri di avvio in un avvio a freddo iOS. launchOptions contiene i dati con cui il sistema ha avviato l’applicazione. Le notifiche, i deep link e i link universali vengono passati attraverso questo dizionario. Lo sviluppatore deve gestire correttamente tutti i possibili scenari di avvio per garantire un’esperienza utente fluida.

Domande frequenti

Cosa succede ai dati quando si passa a Not Running?

I dati salvati nell’archivio persistente (UserDefaults, Core Data, SharedPreferences, Room) vengono conservati. I dati nella RAM — variabili, cache, stato del ViewModel senza SavedStateHandle — vengono persi irrevocabilmente. Pertanto, è di fondamentale importanza salvare lo stato dell’applicazione ad ogni transizione in Background.

Come distinguere un avvio a freddo da uno a caldo su iOS?

Durante un avvio a freddo, viene chiamato application(_:didFinishLaunchingWithOptions:). Durante un avvio a caldo (ritorno da Suspended), questo metodo non viene chiamato — vengono attivati solo applicationWillEnterForeground e applicationDidBecomeActive. Se devi eseguire un’azione solo in un avvio a freddo, imposta un flag in didFinishLaunchingWithOptions.

Un’applicazione Android può essere in Not Running con un Service attivo?

Sì. Un Foreground Service con una notifica persistente impedisce al sistema di terminare il processo, anche se tutte le Activities vengono distrutte. Un Background Service (startService senza foreground) può essere interrotto dal sistema in qualsiasi momento. Un Service in esecuzione significa che il processo esiste, e questo non è più Not Running.

Come emulare Not Running su un simulatore?

Sul simulatore iOS, termina l’app tramite App Switcher (Cmd+Shift+H due volte, swipe verso l’alto). Sull’emulatore Android, usa adb shell am force-stop com.example.app o il pulsante Stop in Logcat. Dopodiché, avvia di nuovo l’applicazione — sarà un avvio a freddo pulito da Not Running.

Cos’è un kill-switch nel contesto di Not Running?

Kill-switch è un comando server per la terminazione di emergenza dell’applicazione. Viene utilizzato in applicazioni bancarie e aziendali per il blocco dell’accesso remoto. Se l’applicazione riceve un comando kill, al successivo avvio a freddo blocca l’interfaccia utente e richiede la riautenticazione. Su iOS, un kill-switch viene implementato tramite notifiche remote con un flag di blocco.

Riepilogo

  • Not Running — lo stato iniziale e finale del ciclo di vita, l’applicazione non è caricata in memoria e non esegue codice
  • Avvio a freddo — riavvio completo dell’applicazione da Not Running, richiede l’inizializzazione di tutti i componenti da zero
  • Avvio a caldo — ritorno da Suspended, non chiama didFinishLaunchingWithOptions né Application.onCreate
  • Salvataggio dati — è di fondamentale importanza eseguirlo durante la transizione in Background, poiché Not Running può verificarsi in qualsiasi momento
  • iOS — applicationWillTerminate non è garantito, lo stato viene salvato tramite UserDefaults o ripristino dello stato
  • Android — il processo può essere terminato in qualsiasi momento, SavedStateHandle in ViewModel salva automaticamente lo stato
  • Ottimizzazione dell’avvio — inizializzazione pigra, lavoro minimo nel thread principale, API SplashScreen per un primo frame veloce

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