Global Exception Handler — een gecentraliseerd mechanisme voor het opvangen van onverwerkte uitzonderingen, dat voorkomt dat een mobiele applicatie crasht. Volgens Apple Developer, 2024 vermindert correcte uitzonderingsafhandeling het aantal crashes met 40–60% en verbetert het de gebruikerservaring. Zonder zo'n handler leidt elke onverwerkte uitzondering in een achtergrondthread tot het onmiddellijk sluiten van de app.
Belangrijkste punten
Global Exception Handler — is een gecentraliseerd mechanisme voor het opvangen van uitzonderingen die niet zijn afgehandeld op het niveau van individuele functies of modules van de applicatie. In de context van mobiele ontwikkeling fungeert zo'n handler als de laatste verdedigingslinie voordat het proces wordt afgebroken.
iOS en Android bieden ingebouwde API's voor het instellen van een globale handler. Apple gebruikt NSSetUncaughtExceptionHandler voor de Objective-C omgeving en Google biedt Thread.setDefaultUncaughtExceptionHandler in Java/Kotlin. Beide mechanismen vangen uitzonderingen op die niet zijn opgevangen door try-catch constructies in alle threads van de applicatie.
Volgens Crashlytics (Google, 2024) vindt ongeveer 25% van de crashes plaats door onverwerkte uitzonderingen in achtergrondthreads — een gebied waar Global Exception Handler bijzonder kritisch is. Ontwikkelaars richten zich vaak op de UI-thread en vergeten asynchrone bewerkingen.
Het gebruik van een globale handler vervangt lokale foutafhandeling niet, maar vult deze aan. Hoofdtaak is maximale informatie opslaan over de toestand van de app op het moment van de uitzondering en correct afsluiten.
Werkingsmechanisme — Global Exception Handler is gebaseerd op het opvangen van besturingssysteemsignalen of runtime-uitzonderingen. Wanneer code een uitzondering genereert die niet door een try-catch blok wordt opgevangen, wordt de controle overgedragen aan de vooraf ingestelde handler.
Op het iOS-platform wordt de handler geregistreerd via NSSetUncaughtExceptionHandler en ontvangt een NSException object met volledige stack trace. Op Android wordt Thread.setDefaultUncaughtExceptionHandler gebruikt, dat Thread en Throwable ontvangt — dit geeft toegang tot het type uitzondering, het bericht en de call-stack.
Na ontvangst van crashgegevens voert de handler drie verplichte acties uit: loggen naar lokale opslag, rapport verzenden naar Crashlytics of Sentry en de app correct afsluiten. Volgens Apple WWDC 2023 is de verwerkingstijd van de handler beperkt tot 5 seconden — daarna beëindigt het systeem het proces geforceerd.
Voor Swift-apps vanaf iOS 13 is Signals API beschikbaar, die niet alleen uitzonderingen verwerkt maar ook besturingssysteemsignalen — SIGABRT, SIGSEGV en SIGBUS, waarmee de dekking van de handler wordt uitgebreid naar laaggeheugenfouten.
Implementatie — de globale handler op iOS vereist het instellen van een C-functie via NSSetUncaughtExceptionHandler. De handler wordt synchroon aangeroepen op het moment van een onverwerkte uitzondering en krijgt de volledige foutcontext.
void handleUncaughtException(NSException exception) {
NSDictionary userInfo = [exception userInfo];
NSArray stackTrace = [exception callStackSymbols];
NSString reason = [exception reason];
// Crashlog opslaan in lokaal bestand
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);
}
Belangrijk kenmerk van de iOS-implementatie: de handler vangt alleen Objective-C uitzonderingen. Swift-fouten die het throw-catch mechanisme gebruiken, komen niet bij deze handler — hiervoor is aparte afhandeling via Swift Error Handling nodig. Vanaf iOS 14 beveelt Apple aan om NSSetUncaughtExceptionHandler te combineren met Signals API voor maximale dekking.
Volgens Apple Technical Note TN2151 moet de app binnen 5 seconden worden beëindigd na het aanroepen van de handler. Elke poging om de uitvoering voort te zetten na terugkeer uit de handler leidt tot ongedefinieerd gedrag en een nieuwe crash.
Android biedt een flexibeler mechanisme voor globale uitzonderingsafhandeling via Thread.setDefaultUncaughtExceptionHandler. De handler ontvangt een verwijzing naar de thread waarin de uitzondering plaatsvond en het Throwable-object zelf.
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, throwable: Throwable) {
// Crashlog opslaan in bestand
val stackTrace = throwable.stackTraceToString()
val crashLog = "CRASH: ${thread.name}\n${stackTrace}"
val file = File(context.cacheDir, "crash_log.txt")
file.writeText(crashLog)
// Verzenden naar Crashlytics
FirebaseCrashlytics.getInstance()
.recordException(throwable)
// Proces beëindigen
android.os.Process.killProcess(
android.os.Process.myPid()
)
}
}
// Installatie in Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(
GlobalExceptionHandler()
)
}
}
Belangrijk verschil van de Android-implementatie: elke thread heeft zijn eigen handler en setDefaultUncaughtExceptionHandler stelt een handler in voor alle threads zonder individuele handler. Dit zorgt voor globale dekking — van de UI-thread tot achtergrond AsyncTask en coroutines.
Vanaf Android 12+ is er een beperking: na het aanroepen van uncaughtException moet de app binnen 100 milliseconden worden beëindigd. Als de handler langdurige bewerkingen uitvoert, kan het systeem het proces doden voordat het loggen is voltooid. Het wordt aanbevolen om een achtergrondservice te gebruiken voor het verzenden van crashrapporten.
Eerste regel — probeer de app niet te herstellen na een onverwerkte uitzondering. De toestand van de app na een crash is ongedefinieerd en verder werken kan leiden tot beschadiging van gebruikersgegevens.
Tijdsbeperking — de belangrijkste technische beperking van Global Exception Handler. Op iOS is dit 5 seconden, op Android — 100 milliseconden. Binnen de handler is alleen het opslaan van minimale gegevens toegestaan: type uitzondering, call-stack en de status van enkele belangrijke variabelen.
Netwerkverzoeken, database writes en complexe serialisatie moeten worden verplaatst naar een uitgesteld mechanisme — bijvoorbeeld loggen naar een bestand en verzenden bij de volgende start van de app.
Crash-reporting diensten — Firebase Crashlytics, Sentry, Bugsnag — installeren zelf een globale handler. Als een ontwikkelaar zijn eigen handler erbovenop installeert, moet hij de controle na zijn acties doorgeven aan het crash-reporting systeem. Op Android wordt handler compositie gebruikt: voer eigen logica uit, roep dan de vorige handler aan.
Voor Firebase Crashlytics wordt aanbevolen om helemaal geen eigen Thread.setDefaultUncaughtExceptionHandler te installeren — Crashlytics SDK doet dit automatisch bij initialisatie.
Gebruikerscontext — naast de standaard call-stack is het nuttig om de app-versie, OS-versie, hoeveelheid vrij geheugen en de tijd tot crash te loggen. Deze gegevens zijn cruciaal voor het reproduceren en oplossen van het probleem.
Op iOS kan NSSetUncaughtExceptionHandler niet alleen worden gebruikt voor het loggen, maar ook voor tijdelijke opslag van gegevens in NSUserDefaults met de synchronize-vlag — dit garandeert dat gegevens behouden blijven, zelfs bij onmiddellijke beëindiging van het proces.
Verplicht testen — Global Exception Handler moet worden getest in elke fase van CI/CD. Op iOS kan een testuitzondering worden geïnitieerd via @throw NSException, op Android via throw RuntimeException(). Er wordt gecontroleerd of de handler wordt aangeroepen, de log wordt opgeslagen en de app correct wordt afgesloten.
Volgens Google I/O 2023 vindt meer dan 30% van de crashes in productie plaats op apparaten die de ontwikkelaar niet heeft getest — verschillende Android-versies, aangepaste firmware, beperkt geheugen.
Eerste en meest voorkomende fout — proberen de app voort te zetten na het afhandelen van een uitzondering. Na het aanroepen van uncaughtException is de app in een instabiele toestand en verdere bewerkingen kunnen een cascade van fouten en gegevensbeschadiging veroorzaken.
Tweede fout — het uitvoeren van lange bewerkingen binnen de handler. Netwerkverzoeken, het schrijven van grote bestanden of complexe berekeningen kunnen niet worden voltooid voordat het proces geforceerd wordt beëindigd. Volgens Apple Technical Q&A QA1468 is het verzenden van een HTTP-verzoek binnen de handler de belangrijkste oorzaak van verloren crashrapporten.
Derde fout — het negeren van achtergrondthreads. Een Global Exception Handler die alleen voor de hoofdthread is ingesteld, beschermt niet tegen crashes in coroutines, DispatchQueue, AsyncTask of RxJava. Op Android moet elke thread zijn eigen handler hebben — en setDefaultUncaughtExceptionHandler lost dit probleem alleen op voor threads zonder individuele handler.
Vierde fout — gebrek aan fallback voor besturingssysteemsignalen. NSSetUncaughtExceptionHandler op iOS vangt SIGABRT, SIGSEGV en SIGBUS niet op. Voor deze signalen is aparte installatie van handlers via sigaction API vereist. Ontwikkelaars komen hier pas achter wanneer hun app crasht zonder enig crashrapport.
Vijfde fout — het loggen van vertrouwelijke gegevens. E-mailadressen, autorisatietokens of persoonlijke gebruikersgegevens komen in de crashlog terecht. Dit schendt GDPR en Apple App Store Review Guidelines. Filter altijd de verzonden gegevens via regex of een whitelist van toegestane velden.
Veelgestelde vragen
Nee — na het aanroepen van Global Exception Handler is de toestand van de app ongedefinieerd. Elke poging om verder te werken kan leiden tot gegevensbeschadiging. De enige correcte actie is het opslaan van de crashlog en het beëindigen van het proces.
Niet alle — op iOS vangt NSSetUncaughtExceptionHandler alleen Objective-C uitzonderingen. Swift-fouten en besturingssysteemsignalen (SIGSEGV, SIGABRT) vereisen aparte handlers. Op Android vangt Thread.setDefaultUncaughtExceptionHandler alle RuntimeException, maar geen native code fouten via JNI.
Sla een verwijzing op naar de vorige handler via Thread.getDefaultUncaughtExceptionHandler() voordat u uw eigen handler installeert. Roep aan het einde van uw handler previousHandler.uncaughtException(thread, throwable) aan — dit garandeert dat Crashlytics of Sentry hun gegevens ontvangen.
Voor native code is signaalverwerking via sigaction() vereist — SIGSEGV, SIGABRT, SIGBUS. Op Android kan Google Breakpad of Crashpad worden gebruikt. Op iOS is vanaf versie 13 Signals API beschikbaar voor het verwerken van mach-uitzonderingen.
Nee — het installeren van de handler heeft alleen invloed op het moment van een uitzondering. In normaal gebruik is er geen overhead. Het enige risico is een geheugenlek als de handler een verwijzing naar Activity of Context vasthoudt, waardoor de garbage collector ze niet kan opruimen.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook