Global Exception Handler — un mécanisme centralisé pour intercepter les exceptions non traitées, empêchant l'arrêt brutal des applications mobiles. Selon Apple Developer, 2024, un traitement correct des exceptions réduit le nombre de crashs de 40–60% et améliore l'expérience utilisateur. Sans un tel gestionnaire, toute exception non traitée dans un thread d'arrière-plan entraîne la fermeture immédiate de l'application.
Points Clés
Global Exception Handler — est un mécanisme centralisé pour intercepter les exceptions qui n'ont pas été traitées au niveau des fonctions ou modules individuels de l'application. Dans le contexte du développement mobile, un tel gestionnaire agit comme la dernière ligne de défense avant l'arrêt anormal du processus.
iOS et Android fournissent des API intégrées pour définir un gestionnaire global. Apple utilise NSSetUncaughtExceptionHandler pour l'environnement Objective-C, tandis que Google propose Thread.setDefaultUncaughtExceptionHandler en Java/Kotlin. Les deux mécanismes interceptent les exceptions qui n'ont pas été capturées par les constructions try-catch sur tous les threads de l'application.
Selon Crashlytics (Google, 2024), environ 25 % des crashs sont dus à des exceptions non traitées dans les threads d'arrière-plan — un domaine où le Global Exception Handler est particulièrement critique. Les développeurs se concentrent souvent sur le thread UI, oubliant les opérations asynchrones.
L'utilisation d'un gestionnaire global ne remplace pas la gestion locale des erreurs, mais la complète. La tâche principale est de sauvegarder un maximum d'informations sur l'état de l'application au moment de l'exception et de se terminer correctement.
Le mécanisme de fonctionnement du Global Exception Handler est basé sur l'interception de signaux du système d'exploitation ou d'exceptions d'exécution. Lorsque le code lève une exception qui n'est capturée par aucun bloc try-catch, le contrôle est transféré à un gestionnaire préenregistré.
Sur iOS, le gestionnaire est enregistré via NSSetUncaughtExceptionHandler et reçoit un objet NSException avec une pile d'appels complète. Sur Android, on utilise Thread.setDefaultUncaughtExceptionHandler, qui accepte Thread et Throwable — fournissant l'accès au type d'exception, au message et à la pile d'appels.
Après avoir reçu les données de crash, le gestionnaire effectue trois actions obligatoires : écrire un journal dans le stockage local, envoyer un rapport à Crashlytics ou Sentry, et terminer l'application correctement. Selon l'Apple WWDC 2023, le temps d'exécution du gestionnaire est limité à 5 secondes — après quoi le système termine le processus de force.
Pour les applications Swift à partir d'iOS 13, la Signals API a été introduite, qui gère non seulement les exceptions mais aussi les signaux du système d'exploitation — SIGABRT, SIGSEGV et SIGBUS, étendant la couverture du gestionnaire aux erreurs mémoire de bas niveau.
L'implémentation d'un gestionnaire global sur iOS nécessite de définir une fonction C via NSSetUncaughtExceptionHandler. Le gestionnaire est appelé de manière synchrone au moment d'une exception non traitée et reçoit le contexte complet de l'erreur.
void handleUncaughtException(NSException exception) {
NSDictionary userInfo = [exception userInfo];
NSArray stackTrace = [exception callStackSymbols];
NSString reason = [exception reason];
// Enregistrer le journal de crash dans un fichier local
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);
}
Une caractéristique importante de l'implémentation iOS : le gestionnaire ne capture que les exceptions Objective-C. Les erreurs Swift utilisant le mécanisme throw-catch n'atteignent pas ce gestionnaire — elles nécessitent un traitement séparé via Swift Error Handling. À partir d'iOS 14, Apple recommande de combiner NSSetUncaughtExceptionHandler avec la Signals API pour une couverture maximale.
Selon Apple Technical Note TN2151, après l'appel du gestionnaire, l'application doit se terminer dans les 5 secondes. Toute tentative de continuer l'exécution après le retour du gestionnaire entraîne un comportement indéfini et un nouveau crash.
Android fournit un mécanisme plus flexible pour la gestion globale des exceptions via Thread.setDefaultUncaughtExceptionHandler. Le gestionnaire reçoit une référence au thread où l'exception s'est produite et l'objet Throwable lui-même.
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, throwable: Throwable) {
// Enregistrer le journal de crash dans un fichier
val stackTrace = throwable.stackTraceToString()
val crashLog = "CRASH: ${thread.name}\n${stackTrace}"
val file = File(context.cacheDir, "crash_log.txt")
file.writeText(crashLog)
// Envoyer à Crashlytics
FirebaseCrashlytics.getInstance()
.recordException(throwable)
// Terminer le processus
android.os.Process.killProcess(
android.os.Process.myPid()
)
}
}
// Configuration dans Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(
GlobalExceptionHandler()
)
}
}
Une différence clé de l'implémentation Android : chaque thread a son propre gestionnaire, et setDefaultUncaughtExceptionHandler définit le gestionnaire pour tous les threads qui n'ont pas de gestionnaire individuel attribué. Cela garantit une couverture globale — du thread UI aux AsyncTask d'arrière-plan et coroutines.
Sur Android 12+, il y a une limitation : après avoir appelé uncaughtException, l'application doit se terminer dans les 100 millisecondes. Si le gestionnaire effectue des opérations longues, le système peut tuer le processus avant que le journal ne soit écrit. Il est recommandé d'utiliser un service d'arrière-plan pour envoyer les rapports de crash.
La première règle — ne tentez pas de restaurer le fonctionnement de l'application après une exception non traitée. L'état de l'application après un crash est indéfini, et continuer l'exécution peut entraîner la corruption des données utilisateur.
Limite de temps — la principale contrainte technique du Global Exception Handler. Sur iOS, elle est de 5 secondes, sur Android — 100 millisecondes. À l'intérieur du gestionnaire, seul un ensemble minimal de données doit être sauvegardé : type d'exception, pile d'appels et état de quelques variables clés.
L'envoi de requêtes réseau, l'écriture dans la base de données et la sérialisation complexe doivent être différés vers un mécanisme différé — par exemple, sauvegarder le journal dans un fichier et l'envoyer au prochain lancement de l'application.
Services de signalement de crash — Firebase Crashlytics, Sentry, Bugsnag — définissent leur propre gestionnaire global. Si un développeur définit un gestionnaire personnalisé supplémentaire, il doit passer le contrôle au système de signalement de crash après ses propres actions. Sur Android, on utilise la composition de gestionnaires : exécuter votre logique, puis appeler le gestionnaire précédent.
Pour Firebase Crashlytics, il est recommandé de ne pas définir de Thread.setDefaultUncaughtExceptionHandler personnalisé — le SDK Crashlytics le fait automatiquement à l'initialisation.
Contexte utilisateur — en plus de la pile d'appels standard, il est utile de journaliser la version de l'application, la version de l'OS, la taille de la mémoire disponible et le temps de fonctionnement avant le crash. Ces données sont cruciales pour reproduire et résoudre le problème.
Sur iOS, on peut utiliser NSSetUncaughtExceptionHandler non seulement pour écrire, mais aussi pour le stockage temporaire dans NSUserDefaults avec le drapeau synchronize — cela garantit la persistance même en cas d'arrêt immédiat du processus.
Test obligatoire — le Global Exception Handler doit être testé à chaque étape du CI/CD. Sur iOS, une exception de test peut être déclenchée via @throw NSException, sur Android — via throw RuntimeException(). Vérifiez que le gestionnaire est appelé, que le journal est sauvegardé et que l'application se termine correctement.
Selon Google I/O 2023, plus de 30 % des crashs en production se produisent sur des appareils que le développeur n'a pas testés — différentes versions d'Android, firmware personnalisé, mémoire limitée.
La première erreur et la plus courante — tenter de continuer l'exécution de l'application après avoir traité une exception. Après avoir appelé uncaughtException, l'application est dans un état instable, et toute opération supplémentaire peut provoquer des erreurs en cascade et une corruption des données.
La deuxième erreur — effectuer des opérations longues à l'intérieur du gestionnaire. Les requêtes réseau, l'écriture de fichiers volumineux ou les calculs complexes ne se terminent pas avant l'arrêt forcé du processus. Selon Apple Technical Q&A QA1468, tenter d'envoyer une requête HTTP à l'intérieur du gestionnaire est la principale cause de perte de rapports de crash.
La troisième erreur — ignorer les threads d'arrière-plan. Un Global Exception Handler défini uniquement pour le thread principal ne protège pas contre les crashs dans les coroutines, DispatchQueue, AsyncTask ou RxJava. Sur Android, chaque thread doit avoir son propre gestionnaire — et setDefaultUncaughtExceptionHandler ne résout cela que pour les threads sans gestionnaire individuel.
La quatrième erreur — absence de repli pour les signaux de l'OS. NSSetUncaughtExceptionHandler sur iOS ne capture pas SIGABRT, SIGSEGV et SIGBUS. Ces signaux nécessitent une configuration séparée de gestionnaires via l'API sigaction. Les développeurs ne le découvrent que lorsque l'application plante sans aucun rapport de crash.
La cinquième erreur — journalisation de données confidentielles. Le journal de crash peut contenir des e-mails, des jetons d'authentification ou des données personnelles des utilisateurs. Cela viole le RGPD et les Directives d'examen de l'App Store d'Apple. Filtrez toujours les données transmises à l'aide d'expressions régulières ou d'une liste blanche de champs autorisés.
Foire aux questions
Non — après l'invocation d'un Global Exception Handler, l'état de l'application est indéfini. Toute tentative de continuer l'exécution peut entraîner une corruption des données. La seule action correcte est de sauvegarder le journal de crash et de terminer le processus.
Pas tous — sur iOS, NSSetUncaughtExceptionHandler ne capture que les exceptions Objective-C. Les erreurs Swift et les signaux de l'OS (SIGSEGV, SIGABRT) nécessitent des gestionnaires séparés. Sur Android, Thread.setDefaultUncaughtExceptionHandler capture toutes les RuntimeExceptions mais pas les erreurs de code natif via JNI.
Sauvegardez une référence au gestionnaire précédent via Thread.getDefaultUncaughtExceptionHandler() avant de définir le vôtre. À la fin de votre gestionnaire, appelez previousHandler.uncaughtException(thread, throwable) — cela garantit que Crashlytics ou Sentry reçoivent leurs données.
Pour le code natif, un traitement des signaux via sigaction() est nécessaire — SIGSEGV, SIGABRT, SIGBUS. Sur Android, vous pouvez utiliser Google Breakpad ou Crashpad. Sur iOS à partir de la version 13, la Signals API est disponible pour gérer les exceptions mach.
Non — la définition du gestionnaire n'affecte que le moment où une exception se produit. En fonctionnement normal de l'application, il n'y a pas de surcharge. Le seul risque est une fuite mémoire si le gestionnaire conserve une référence à une Activity ou un Context, empêchant le ramasse-miettes.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi