Global Exception Handler: суть, принцип работы и реализация в проектах

Автор: IT Sectr Опубликовано: 2026-05-27 Время чтения: 8 мин

Global Exception Handler — механизм централизованного перехвата необработанных исключений, предотвращающий аварийное завершение мобильного приложения. По данным Apple Developer, 2024, корректная обработка исключений снижает количество crash-падений на 40–60% и улучшает пользовательский опыт. Без такого обработчика любое необработанное исключение в фоновом потоке приводит к немедленному закрытию приложения.

Главное

  • Global Exception Handler — точка сбора всех необработанных исключений в приложении, предотвращающая crash
  • iOS NSSetUncaughtExceptionHandler — C-функция для перехвата Objective-C исключений на платформе Apple
  • Android Thread.setDefaultUncaughtExceptionHandler — встроенный механизм платформы для глобального перехвата исключений
  • Логирование перед закрытием — основная задача обработчика: сохранить информацию о crash до завершения процесса
  • Graceful degradation — обработчик позволяет показать пользователю корректный экран ошибки вместо аварийного закрытия

Что такое Global Exception Handler?

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, расширяя зону покрытия обработчика на низкоуровневые ошибки памяти.

Реализация Global Exception Handler на iOS

Реализация глобального обработчика на iOS требует установки C-функции через NSSetUncaughtExceptionHandler. Обработчик вызывается синхронно в момент необработанного исключения и получает полный контекст ошибки.

objective-c
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.

Реализация Global Exception Handler на Android

Android предоставляет более гибкий механизм глобальной обработки исключений через Thread.setDefaultUncaughtExceptionHandler. Обработчик получает ссылку на поток, в котором произошло исключение, и сам объект Throwable.

kotlin
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-отчёта.

Best practices при работе с Global Exception Handler

Первое правило — не пытаться восстановить работу приложения после необработанного исключения. Состояние приложения после crash не определено, и продолжение работы может привести к повреждению данных пользователя.

Минимизация времени работы обработчика

Ограничение по времени — главное техническое ограничение Global Exception Handler. На iOS это 5 секунд, на Android — 100 миллисекунд. Внутри обработчика допустимо только сохранить минимальный набор данных: тип исключения, стек вызовов и состояние пары ключевых переменных.

Отправку сетевых запросов, запись в базу данных и сложную сериализацию следует перенести в отложенный механизм — например, сохранить лог в файл и отправить его при следующем запуске приложения.

Комбинирование с crash-reporting системами

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-лог попадают email, токены авторизации или персональные данные пользователей. Это нарушает GDPR и Apple App Store Review Guidelines. Всегда фильтруйте передаваемые данные через regex или whitelist-список разрешённых полей.

Часто задаваемые вопросы

Можно ли восстановить работу приложения после глобального исключения?

Нет — после вызова Global Exception Handler состояние приложения не определено. Любая попытка продолжить работу может привести к повреждению данных. Единственное корректное действие — сохранить crash-лог и завершить процесс.

Global Exception Handler перехватывает все типы ошибок?

Не все — на iOS NSSetUncaughtExceptionHandler ловит только Objective-C исключения. Swift-ошибки и сигналы ОС (SIGSEGV, SIGABRT) требуют отдельных обработчиков. На Android Thread.setDefaultUncaughtExceptionHandler перехватывает все RuntimeException, но не ошибки native-кода через JNI.

Как передать управление crash-reporting системе после своего обработчика?

Сохраните ссылку на предыдущий обработчик через Thread.getDefaultUncaughtExceptionHandler() перед установкой своего. В конце своего обработчика вызовите previousHandler.uncaughtException(thread, throwable) — это гарантирует, что Crashlytics или Sentry получат свои данные.

Что делать, если crash произошёл в C/C++ native коде?

Для native-кода требуется обработка сигналов через sigaction() — SIGSEGV, SIGABRT, SIGBUS. На Android можно использовать Google Breakpad или Crashpad. На iOS начиная с версии 13 появился Signals API для обработки mach-исключений.

Может ли Global Exception Handler повлиять на производительность?

Нет — установка обработчика влияет только на момент возникновения исключения. В нормальной работе приложения overhead отсутствует. Единственный риск — утечка памяти, если обработчик хранит ссылку на Activity или Context, предотвращая их сборку мусором.

Итоги

  • Global Exception Handler — последняя линия обороны перед crash, обязателен в любом production-приложении
  • iOS NSSetUncaughtExceptionHandler перехватывает Objective-C исключения с ограничением 5 секунд на обработку
  • Android Thread.setDefaultUncaughtExceptionHandler работает для всех потоков без персонального обработчика
  • Время работы обработчика минимально — сохранить данные и завершить процесс без попытки восстановления
  • Сигналы ОС (SIGSEGV, SIGABRT) не перехватываются стандартными обработчиками — требуется sigaction API
  • Crash-reporting системы следует вызывать через композицию обработчиков, передавая управление после своей логики
  • Тестирование обработчика в CI/CD — обязательный этап, предотвращающий потерю crash-отчётов в продакшене

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также