Global Exception Handler — механизам централизованог пресретања необрађених изузетака који спречава аваријско затварање мобилне апликације. Према подацима Apple Developer, 2024, коректна обрада изузетака смањује број crash-падова за 40–60% и побољшава корисничко искуство. Без таквог хендлера, сваки необрађени изузетак у позадинској нити доводи до тренутног затварања апликације.
Главно
Global Exception Handler — је централизовани механизам пресретања изузетака који нису обрађени на нивоу појединачних функција или модула апликације. У контексту мобилног развоја, такав хендлер представља последњу линију одбране пре аваријског завршетка процеса.
iOS и Android пружају уграђене API-је за постављање глобалног хендлера. Apple користи NSSetUncaughtExceptionHandler за окружење Objective-C, а Google нуди Thread.setDefaultUncaughtExceptionHandler у Java/Kotlin. Оба механизма пресрећу изузетке који нису ухваћени конструкцијама try-catch у свим нитима апликације.
Према подацима Crashlytics (Google, 2024), око 25% crash-ева настаје услед необрађених изузетака у позадинским нитима — област у којој је Global Exception Handler посебно критичан. Програмери се често фокусирају на UI нит, заборављајући на асинхроне операције.
Коришћење глобалног хендлера не замењује локалну обраду грешака, већ је допуњује. Главни задатак је сачувати максимум информација о стању апликације у тренутку изузетка и коректно завршити рад.
Механизам рада Global Exception Handler-а се заснива на пресретању сигнала оперативног система или изузетака времена извршења. Када код баци изузетак који није ухваћен ниједним try-catch блоком, управљање се преноси унапред постављеном хендлеру.
На платформи iOS, хендлер се региструје кроз NSSetUncaughtExceptionHandler и прима објекат NSException са комплетним стек-трагом. На Android-у се користи Thread.setDefaultUncaughtExceptionHandler, који прима Thread и Throwable — ово даје приступ типу изузетка, поруци и стеку позива.
Након пријема података о crash-у, хендлер извршава три обавезне радње: упис лога у локално складиште, слање извештаја у Crashlytics или Sentry и коректно затварање апликације. Према Apple WWDC 2023, време рада хендлера је ограничено на 5 секунди — након тога систем присилно завршава процес.
За Swift апликације од iOS 13 појавио се Signals API, који обрађује не само изузетке већ и сигнале оперативног система — SIGABRT, SIGSEGV и SIGBUS, проширујући зону покривености хендлера на нисконвојске грешке меморије.
Имплементација глобалног хендлера на iOS-у захтева постављање C-функције кроз NSSetUncaughtExceptionHandler. Хендлер се позива синхроно у тренутку необрађеног изузетка и прима комплетан контекст грешке.
void handleUncaughtException(NSException exception) {
NSDictionary userInfo = [exception userInfo];
NSArray stackTrace = [exception callStackSymbols];
NSString reason = [exception reason];
// Чувамо crash лог у локалну датотеку
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);
}
Важна карактеристика iOS имплементације: хендлер пресреће само Objective-C изузетке. Swift грешке, које користе механизам throw-catch, не стижу до овог хендлера — за њих је потребна посебна обрада кроз Swift Error Handling. Од iOS 14, Apple препоручује комбиновање NSSetUncaughtExceptionHandler са Signals API за максималну покривеност.
Према Apple Technical Note TN2151, након позива хендлера, апликација се мора завршити у року од 5 секунди. Сваки покушај наставка извршења након повратка из хендлера доводи до неодређеног понашања и поновног crash-а.
Android пружа флексибилнији механизам глобалне обраде изузетака кроз Thread.setDefaultUncaughtExceptionHandler. Хендлер прима референцу на нит у којој се догодио изузетак и сам објекат Throwable.
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, throwable: Throwable) {
// Чувамо crash лог у датотеку
val stackTrace = throwable.stackTraceToString()
val crashLog = "CRASH: ${thread.name}\n${stackTrace}"
val file = File(context.cacheDir, "crash_log.txt")
file.writeText(crashLog)
// Шаљемо у Crashlytics
FirebaseCrashlytics.getInstance()
.recordException(throwable)
// Завршавамо процес
android.os.Process.killProcess(
android.os.Process.myPid()
)
}
}
// Инсталација у Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(
GlobalExceptionHandler()
)
}
}
Кључна разлика Android имплементације: свака нит има сопствени хендлер, а setDefaultUncaughtExceptionHandler поставља хендлер за све нити које немају индивидуални хендлер. Ово обезбеђује глобалну покривеност — од UI нити до позадинских AsyncTask и корутина.
На Android 12+ појавило се ограничење: након позива uncaughtException, апликација се мора завршити у року од 100 милисекунди. Ако хендлер извршава дуготрајне операције, систем може убити процес пре завршетка уписа лога. Препоручује се коришћење позадинског сервиса за слање crash извештаја.
Прво правило — не покушавати обновити рад апликације након необрађеног изузетка. Стање апликације након crash-а је неодређено, а наставак рада може довести до оштећења корисничких података.
Временско ограничење — главно техничко ограничење Global Exception Handler-а. На iOS-у је то 5 секунди, на Android-у — 100 милисекунди. Унутар хендлера је дозвољено само сачувати минимални скуп података: тип изузетка, стек позива и стање неколико кључних променљивих.
Слање мрежних захтева, упис у базу података и сложену серијализацију треба преместити у одложени механизам — на пример, сачувати лог у датотеку и послати га при следећем покретању апликације.
Crash-reporting сервиси — Firebase Crashlytics, Sentry, Bugsnag — сами постављају глобални хендлер. Ако програмер поставља сопствени хендлер преко њих, потребно је пренети управљање crash-reporting систему након својих радњи. На Android-у се користи композиција хендлера: извршити своју логику, затим позвати претходни хендлер.
За Firebase Crashlytics се препоручује да се уопште не поставља сопствени Thread.setDefaultUncaughtExceptionHandler — Crashlytics SDK то ради аутоматски при иницијализацији.
Кориснички контекст — поред стандардног стека позива, корисно је логирати верзију апликације, верзију ОС-а, количину слободне меморије и време рада до crash-а. Ови подаци су критични за репродукцију и отклањање проблема.
У iOS-у се може користити NSSetUncaughtExceptionHandler не само за упис, већ и за привремено чување података у NSUserDefaults са флагом synchronize — ово гарантује очување података чак и при тренутном завршетку процеса.
Обавезно тестирање — Global Exception Handler мора бити тестиран на свакој фази CI/CD-а. На iOS-у се може иницирати тестни изузетак кроз @throw NSException, на Android-у — кроз throw RuntimeException(). Проверава се да је хендлер позван, лог сачуван и апликација коректно завршена.
Према Google I/O 2023, више од 30% crash-ева у продукцији се дешава на уређајима које програмер није тестирао — различите верзије Android-а, прилагођени фирмвер, ограничена меморија.
Прва и најчешћа грешка — покушај наставка рада апликације након обраде изузетка. Након позива uncaughtException, апликација се налази у нестабилном стању, а све даље операције могу изазвати каскаду грешака и оштећење података.
Друга грешка — извршавање дуготрајних операција унутар хендлера. Мрежни захтеви, упис великих датотека или сложена израчунавања не стижу да се заврше пре присилног завршетка процеса. Према Apple Technical Q&A QA1468, покушај слања HTTP захтева унутар хендлера је водећи узрок изгубљених crash извештаја.
Трећа грешка — игнорисање позадинских нити. Global Exception Handler постављен само за главну нит не штити од crash-ева у корутинама, DispatchQueue, AsyncTask или RxJava. На Android-у свака нит треба да има свој хендлер — а setDefaultUncaughtExceptionHandler решава овај проблем само за нити без индивидуалног хендлера.
Четврта грешка — недостатак fallback-а за сигнале оперативног система. NSSetUncaughtExceptionHandler на iOS-у не пресреће SIGABRT, SIGSEGV и SIGBUS. За ове сигнале је потребно посебно постављање хендлера кроз sigaction API. Програмери сазнају за ово тек када апликација падне без иједног crash извештаја.
Пета грешка — логирање поверљивих података. У crash лог доспевају имејлови, токени за ауторизацију или лични подаци корисника. Ово крши GDPR и Apple App Store Review Guidelines. Увек филтрирајте прослеђене податке кроз regex или whitelist листу дозвољених поља.
Често постављана питања
Не — након позива Global Exception Handler-а стање апликације је неодређено. Сваки покушај наставка рада може довести до оштећења података. Једина коректна радња — сачувати crash лог и завршити процес.
Не све — на iOS-у NSSetUncaughtExceptionHandler хвата само Objective-C изузетке. Swift грешке и сигнали ОС-а (SIGSEGV, SIGABRT) захтевају посебне хендлере. На Android-у Thread.setDefaultUncaughtExceptionHandler пресреће све RuntimeException, али не и грешке native кода кроз JNI.
Сачувајте референцу на претходни хендлер кроз Thread.getDefaultUncaughtExceptionHandler() пре постављања свог. На крају свог хендлера позовите previousHandler.uncaughtException(thread, throwable) — ово гарантује да ће Crashlytics или Sentry добити своје податке.
За native код је потребна обрада сигнала кроз sigaction() — SIGSEGV, SIGABRT, SIGBUS. На Android-у се може користити Google Breakpad или Crashpad. На iOS-у од верзије 13 појавио се Signals API за обраду mach изузетака.
Не — постављање хендлера утиче само на тренутак настанка изузетка. У нормалном раду апликације overhead не постоји. Једини ризик је цурење меморије ако хендлер чува референцу на Activity или Context, спречавајући њихово сакупљање смећа.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође