Global Exception Handler: essenza, principio di funzionamento e implementazione nei progetti

Autore: IT Sectr Pubblicato: 2026-05-27 Tempo di lettura: 8 min

Global Exception Handler — un meccanismo centralizzato per intercettare le eccezioni non gestite che impedisce l'arresto anomalo delle applicazioni mobili. Secondo Apple Developer, 2024, la corretta gestione delle eccezioni riduce il numero di crash del 40–60% e migliora l'esperienza utente. Senza un tale gestore, qualsiasi eccezione non gestita in un thread in background porta alla chiusura immediata dell'applicazione.

Punti Chiave

  • Global Exception Handler — un punto di raccolta centrale per tutte le eccezioni non gestite nell'applicazione, che previene i crash
  • iOS NSSetUncaughtExceptionHandler — una funzione C per intercettare le eccezioni Objective-C sulla piattaforma Apple
  • Android Thread.setDefaultUncaughtExceptionHandler — un meccanismo integrato della piattaforma per l'intercettazione globale delle eccezioni
  • Registrazione prima della chiusura — il compito principale del gestore: salvare le informazioni sul crash prima della terminazione del processo
  • Degradazione graduale — il gestore consente di mostrare all'utente una schermata di errore appropriata invece di un arresto improvviso

Cos'è un Global Exception Handler?

Global Exception Handler — è un meccanismo centralizzato per intercettare le eccezioni che non sono state gestite a livello di singole funzioni o moduli dell'applicazione. Nel contesto dello sviluppo mobile, tale gestore funge da ultima linea di difesa prima della terminazione anomala del processo.

iOS e Android forniscono API integrate per impostare un gestore globale. Apple utilizza NSSetUncaughtExceptionHandler per l'ambiente Objective-C, mentre Google offre Thread.setDefaultUncaughtExceptionHandler in Java/Kotlin. Entrambi i meccanismi intercettano le eccezioni che non sono state catturate dai costrutti try-catch su tutti i thread dell'applicazione.

Secondo Crashlytics (Google, 2024), circa il 25% dei crash si verifica a causa di eccezioni non gestite nei thread in background — un'area in cui il Global Exception Handler è particolarmente critico. Gli sviluppatori si concentrano spesso sul thread UI, dimenticando le operazioni asincrone.

Usare un gestore globale non sostituisce la gestione locale degli errori, ma la completa. Il compito principale è salvare il massimo delle informazioni sullo stato dell'applicazione al momento dell'eccezione e terminare correttamente.

Come funziona un gestore globale di eccezioni

Il meccanismo di funzionamento del Global Exception Handler si basa sull'intercettazione di segnali del sistema operativo o eccezioni in fase di esecuzione. Quando il codice lancia un'eccezione che non viene catturata da alcun blocco try-catch, il controllo viene trasferito a un gestore precedentemente registrato.

Su iOS, il gestore viene registrato tramite NSSetUncaughtExceptionHandler e riceve un oggetto NSException con uno stack trace completo. Su Android, si utilizza Thread.setDefaultUncaughtExceptionHandler, che accetta Thread e Throwable — fornendo accesso al tipo di eccezione, al messaggio e allo stack delle chiamate.

Dopo aver ricevuto i dati del crash, il gestore esegue tre azioni obbligatorie: scrivere un log nella memoria locale, inviare un report a Crashlytics o Sentry, e terminare correttamente l'applicazione. Secondo Apple WWDC 2023, il tempo di esecuzione del gestore è limitato a 5 secondi — dopo di che il sistema termina forzatamente il processo.

Per le applicazioni Swift a partire da iOS 13, è stata introdotta la Signals API, che gestisce non solo le eccezioni ma anche i segnali del sistema operativo — SIGABRT, SIGSEGV e SIGBUS, estendendo la copertura del gestore agli errori di memoria di basso livello.

Implementazione di Global Exception Handler su iOS

L'implementazione di un gestore globale su iOS richiede l'impostazione di una funzione C tramite NSSetUncaughtExceptionHandler. Il gestore viene chiamato in modo sincrono al momento di un'eccezione non gestita e riceve il contesto completo dell'errore.

objective-c
void handleUncaughtException(NSException exception) {
    NSDictionary userInfo = [exception userInfo];
    NSArray stackTrace = [exception callStackSymbols];
    NSString reason = [exception reason];

    // Salva il log del crash in un file locale
    NSString logPath = [NSSearchPathForDirectoriesInDomains(
        NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    [exceptionLog writeToFile:logPath atomically:YES];
}

int main(int argc, char argv[]) {
    NSSetUncaughtExceptionHandler(&handleUncaughtException);
    return UIApplicationMain(argc, argv, nil, nil);
}

Una caratteristica importante dell'implementazione iOS: il gestore cattura solo eccezioni Objective-C. Gli errori Swift che utilizzano il meccanismo throw-catch non raggiungono questo gestore — richiedono una gestione separata tramite Swift Error Handling. A partire da iOS 14, Apple raccomanda di combinare NSSetUncaughtExceptionHandler con la Signals API per la massima copertura.

Secondo Apple Technical Note TN2151, dopo la chiamata del gestore, l'applicazione deve terminare entro 5 secondi. Qualsiasi tentativo di continuare l'esecuzione dopo il ritorno dal gestore porta a un comportamento indefinito e a un successivo crash.

Implementazione di Global Exception Handler su Android

Android fornisce un meccanismo più flessibile per la gestione globale delle eccezioni tramite Thread.setDefaultUncaughtExceptionHandler. Il gestore riceve un riferimento al thread in cui si è verificata l'eccezione e l'oggetto Throwable stesso.

kotlin
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        // Salva il log del crash in un file
        val stackTrace = throwable.stackTraceToString()
        val crashLog = "CRASH: ${thread.name}\n${stackTrace}"

        val file = File(context.cacheDir, "crash_log.txt")
        file.writeText(crashLog)

        // Invia a Crashlytics
        FirebaseCrashlytics.getInstance()
            .recordException(throwable)

        // Termina il processo
        android.os.Process.killProcess(
            android.os.Process.myPid()
        )
    }
}

// Configurazione in Application.onCreate
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Thread.setDefaultUncaughtExceptionHandler(
            GlobalExceptionHandler()
        )
    }
}

Una differenza chiave dell'implementazione Android: ogni thread ha il proprio gestore e setDefaultUncaughtExceptionHandler imposta il gestore per tutti i thread che non hanno un gestore individuale assegnato. Ciò garantisce una copertura globale — dal thread UI agli AsyncTask in background e alle coroutine.

Su Android 12+, c'è una limitazione: dopo aver chiamato uncaughtException, l'applicazione deve terminare entro 100 millisecondi. Se il gestore esegue operazioni lunghe, il sistema potrebbe uccidere il processo prima che il log venga scritto. Si consiglia di utilizzare un servizio in background per l'invio dei report di crash.

Best practice per Global Exception Handler

La prima regola — non tentare di ripristinare il funzionamento dell'applicazione dopo un'eccezione non gestita. Lo stato dell'applicazione dopo un crash è indefinito e continuare l'esecuzione può portare al danneggiamento dei dati utente.

Minimizzare il tempo di esecuzione del gestore

Limite di tempo — il principale vincolo tecnico del Global Exception Handler. Su iOS è di 5 secondi, su Android — 100 millisecondi. All'interno del gestore, dovrebbe essere salvato solo un insieme minimo di dati: tipo di eccezione, stack delle chiamate e lo stato di alcune variabili chiave.

L'invio di richieste di rete, la scrittura nel database e la serializzazione complessa dovrebbero essere rinviati a un meccanismo differito — ad esempio, salvare il log in un file e inviarlo al successivo avvio dell'applicazione.

Combinazione con sistemi di crash-reporting

Servizi di crash-reporting — Firebase Crashlytics, Sentry, Bugsnag — impostano il proprio gestore globale. Se uno sviluppatore imposta un gestore personalizzato aggiuntivo, deve passare il controllo al sistema di crash-reporting dopo le proprie azioni. Su Android si utilizza la composizione dei gestori: eseguire la propria logica, quindi chiamare il gestore precedente.

Per Firebase Crashlytics, si raccomanda di non impostare affatto un Thread.setDefaultUncaughtExceptionHandler personalizzato — l'SDK Crashlytics lo fa automaticamente all'inizializzazione.

Registrazione di informazioni aggiuntive

Contesto utente — oltre allo stack delle chiamate standard, è utile registrare la versione dell'applicazione, la versione del sistema operativo, la dimensione della memoria disponibile e il tempo di attività prima del crash. Questi dati sono fondamentali per riprodurre e risolvere il problema.

Su iOS, è possibile utilizzare NSSetUncaughtExceptionHandler non solo per scrivere, ma anche per l'archiviazione temporanea in NSUserDefaults con il flag synchronize — ciò garantisce la persistenza anche in caso di terminazione immediata del processo.

Test del gestore prima del rilascio

Test obbligatorio — il Global Exception Handler deve essere testato in ogni fase del CI/CD. Su iOS, è possibile attivare un'eccezione di test tramite @throw NSException, su Android — tramite throw RuntimeException(). Verificare che il gestore venga chiamato, il log venga salvato e l'applicazione termini correttamente.

Secondo Google I/O 2023, più del 30% dei crash in produzione si verifica su dispositivi che lo sviluppatore non ha testato — diverse versioni di Android, firmware personalizzato, memoria limitata.

Errori comuni nell'uso del gestore

Il primo errore e il più comune — tentare di continuare l'esecuzione dell'applicazione dopo aver gestito un'eccezione. Dopo aver chiamato uncaughtException, l'applicazione è in uno stato instabile e qualsiasi ulteriore operazione può causare errori a cascata e danneggiamento dei dati.

Il secondo errore — eseguire operazioni lunghe all'interno del gestore. Le richieste di rete, la scrittura di file grandi o calcoli complessi non vengono completati prima della terminazione forzata del processo. Secondo Apple Technical Q&A QA1468, tentare di inviare una richiesta HTTP all'interno del gestore è la causa principale della perdita di report di crash.

Il terzo errore — ignorare i thread in background. Un Global Exception Handler impostato solo per il thread principale non protegge dai crash in coroutine, DispatchQueue, AsyncTask o RxJava. Su Android, ogni thread dovrebbe avere il proprio gestore — e setDefaultUncaughtExceptionHandler risolve questo solo per i thread senza un gestore individuale.

Il quarto errore — mancanza di fallback per i segnali del sistema operativo. NSSetUncaughtExceptionHandler su iOS non cattura SIGABRT, SIGSEGV e SIGBUS. Questi segnali richiedono l'impostazione separata di gestori tramite l'API sigaction. Gli sviluppatori lo scoprono solo quando l'app si blocca senza un singolo report di crash.

Il quinto errore — registrazione di dati riservati. Il log di crash può contenere email, token di autenticazione o dati personali degli utenti. Ciò viola il GDPR e le Linee guida per la revisione dell'App Store di Apple. Filtrare sempre i dati trasmessi utilizzando regex o una whitelist di campi consentiti.

Domande Frequenti

È possibile ripristinare l'applicazione dopo un'eccezione globale?

No — dopo l'invocazione del Global Exception Handler, lo stato dell'applicazione è indefinito. Qualsiasi tentativo di continuare l'esecuzione può portare al danneggiamento dei dati. L'unica azione corretta è salvare il log del crash e terminare il processo.

Il Global Exception Handler cattura tutti i tipi di errori?

Non tutti — su iOS, NSSetUncaughtExceptionHandler cattura solo eccezioni Objective-C. Gli errori Swift e i segnali del sistema operativo (SIGSEGV, SIGABRT) richiedono gestori separati. Su Android, Thread.setDefaultUncaughtExceptionHandler cattura tutte le RuntimeExceptions ma non gli errori di codice nativo tramite JNI.

Come passare il controllo a un sistema di crash-reporting dopo il mio gestore?

Salvare un riferimento al gestore precedente tramite Thread.getDefaultUncaughtExceptionHandler() prima di impostare il proprio. Alla fine del proprio gestore, chiamare previousHandler.uncaughtException(thread, throwable) — ciò garantisce che Crashlytics o Sentry ricevano i loro dati.

Cosa fare se il crash si verifica in codice nativo C/C++?

Per il codice nativo, è necessaria la gestione dei segnali tramite sigaction() — SIGSEGV, SIGABRT, SIGBUS. Su Android, è possibile utilizzare Google Breakpad o Crashpad. Su iOS a partire dalla versione 13, è disponibile la Signals API per la gestione delle eccezioni mach.

Il Global Exception Handler può influire sulle prestazioni?

No — l'impostazione del gestore influisce solo sul momento in cui si verifica un'eccezione. Nel normale funzionamento dell'applicazione, non c'è overhead. L'unico rischio è una perdita di memoria se il gestore mantiene un riferimento a un'Activity o Context, impedendo la garbage collection.

Riepilogo

  • Global Exception Handler — l'ultima linea di difesa prima di un crash, obbligatorio in qualsiasi applicazione di produzione
  • iOS NSSetUncaughtExceptionHandler cattura eccezioni Objective-C con un limite di elaborazione di 5 secondi
  • Android Thread.setDefaultUncaughtExceptionHandler funziona per tutti i thread senza gestore personale
  • Tempo di esecuzione del gestore minimo — salvare i dati e terminare il processo senza tentare il ripristino
  • Segnali del sistema operativo (SIGSEGV, SIGABRT) non vengono catturati dai gestori standard — è necessaria l'API sigaction
  • Sistemi di crash-reporting dovrebbero essere richiamati tramite composizione dei gestori, passando il controllo dopo la propria logica
  • Test del gestore in CI/CD — fase obbligatoria per evitare la perdita di report di crash in produzione

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