Global Exception Handler: esencia, principio de funcionamiento e implementación en proyectos

Autor: IT Sectr Publicado: 2026-05-27 Tiempo de lectura: 8 min

Global Exception Handler — un mecanismo centralizado para capturar excepciones no manejadas que evita el cierre inesperado de aplicaciones móviles. Según Apple Developer, 2024, el manejo adecuado de excepciones reduce la cantidad de fallos en un 40–60% y mejora la experiencia del usuario. Sin dicho controlador, cualquier excepción no capturada en un hilo en segundo plano provoca el cierre inmediato de la aplicación.

Puntos Clave

  • Global Exception Handler — un punto central de recolección de todas las excepciones no capturadas en la aplicación, que previene fallos
  • iOS NSSetUncaughtExceptionHandler — una función C para interceptar excepciones de Objective-C en la plataforma Apple
  • Android Thread.setDefaultUncaughtExceptionHandler — un mecanismo integrado de la plataforma para la captura global de excepciones
  • Registro antes del cierre — la tarea principal del controlador: guardar información del fallo antes de que finalice el proceso
  • Degradación gradual — el controlador permite mostrar al usuario una pantalla de error adecuada en lugar de un cierre abrupto

¿Qué es un Global Exception Handler?

Global Exception Handler — es un mecanismo centralizado para capturar excepciones que no fueron manejadas a nivel de funciones o módulos individuales de la aplicación. En el contexto del desarrollo móvil, dicho controlador actúa como la última línea de defensa antes de la terminación anormal del proceso.

iOS y Android proporcionan API integradas para establecer un controlador global. Apple utiliza NSSetUncaughtExceptionHandler para el entorno Objective-C, mientras que Google ofrece Thread.setDefaultUncaughtExceptionHandler en Java/Kotlin. Ambos mecanismos capturan excepciones que no fueron atrapadas por construcciones try-catch en todos los hilos de la aplicación.

Según Crashlytics (Google, 2024), aproximadamente el 25% de los fallos ocurren debido a excepciones no capturadas en hilos en segundo plano, un área donde el Global Exception Handler es especialmente crítico. Los desarrolladores a menudo se centran en el hilo de UI, olvidando las operaciones asincrónicas.

El uso de un controlador global no reemplaza el manejo local de errores, sino que lo complementa. La tarea principal es guardar la máxima información sobre el estado de la aplicación en el momento de la excepción y finalizar correctamente.

Cómo funciona un controlador global de excepciones

El mecanismo de funcionamiento de Global Exception Handler se basa en la intercepción de señales del sistema operativo o excepciones en tiempo de ejecución. Cuando el código lanza una excepción que no es capturada por ningún bloque try-catch, el control se transfiere a un controlador previamente registrado.

En iOS, el controlador se registra mediante NSSetUncaughtExceptionHandler y recibe un objeto NSException con un stack trace completo. En Android, se utiliza Thread.setDefaultUncaughtExceptionHandler que acepta Thread y Throwable, proporcionando acceso al tipo de excepción, mensaje y pila de llamadas.

Después de recibir los datos del fallo, el controlador realiza tres acciones obligatorias: escribir un registro en el almacenamiento local, enviar un informe a Crashlytics o Sentry, y finalizar correctamente la aplicación. Según Apple WWDC 2023, el tiempo de ejecución del controlador está limitado a 5 segundos; después de eso, el sistema finaliza el proceso de forma forzada.

Para aplicaciones Swift a partir de iOS 13, se ha introducido la Signals API que maneja no solo excepciones sino también señales del sistema operativo (SIGABRT, SIGSEGV, SIGBUS), ampliando la cobertura del controlador a errores de memoria de bajo nivel.

Implementación de Global Exception Handler en iOS

La implementación de un controlador global en iOS requiere configurar una función C mediante NSSetUncaughtExceptionHandler. El controlador se invoca de forma síncrona en el momento de una excepción no capturada y recibe el contexto completo del error.

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

    // Guardar registro de fallo en archivo 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);
}

Una característica importante de la implementación en iOS: el controlador captura solo excepciones de Objective-C. Los errores de Swift que utilizan el mecanismo throw-catch no llegan a este controlador; requieren un manejo separado a través de Swift Error Handling. A partir de iOS 14, Apple recomienda combinar NSSetUncaughtExceptionHandler con Signals API para obtener la máxima cobertura.

Según Apple Technical Note TN2151, después de llamar al controlador, la aplicación debe finalizar en un plazo de 5 segundos. Cualquier intento de continuar la ejecución después de regresar del controlador provoca un comportamiento indefinido y un nuevo fallo.

Implementación de Global Exception Handler en Android

Android proporciona un mecanismo más flexible para el manejo global de excepciones a través de Thread.setDefaultUncaughtExceptionHandler. El controlador recibe una referencia al hilo donde ocurrió la excepción y el objeto Throwable en sí.

kotlin
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        // Guardar registro de fallo en archivo
        val stackTrace = throwable.stackTraceToString()
        val crashLog = "CRASH: ${thread.name}\n${stackTrace}"

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

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

        // Finalizar proceso
        android.os.Process.killProcess(
            android.os.Process.myPid()
        )
    }
}

// Configuración en Application.onCreate
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Thread.setDefaultUncaughtExceptionHandler(
            GlobalExceptionHandler()
        )
    }
}

Una diferencia clave de la implementación en Android: cada hilo tiene su propio controlador, y setDefaultUncaughtExceptionHandler establece el controlador para todos los hilos que no tienen uno individual asignado. Esto garantiza una cobertura global, desde el hilo de UI hasta AsyncTask en segundo plano y corrutinas.

En Android 12+, existe una limitación: después de llamar a uncaughtException, la aplicación debe finalizar en un plazo de 100 milisegundos. Si el controlador realiza operaciones prolongadas, el sistema puede eliminar el proceso antes de que se escriba el registro. Se recomienda utilizar un servicio en segundo plano para enviar informes de fallos.

Mejores prácticas con Global Exception Handler

La primera regla — no intentar restaurar el funcionamiento de la aplicación después de una excepción no capturada. El estado de la aplicación tras un fallo no está definido, y continuar la ejecución puede provocar la corrupción de los datos del usuario.

Minimizar el tiempo de ejecución del controlador

Límite de tiempo — la principal restricción técnica del Global Exception Handler. En iOS es de 5 segundos, en Android de 100 milisegundos. Dentro del controlador, solo se debe guardar un conjunto mínimo de datos: tipo de excepción, pila de llamadas y el estado de algunas variables clave.

El envío de solicitudes de red, la escritura en la base de datos y la serialización compleja deben diferirse a un mecanismo diferido, por ejemplo, guardar el registro en un archivo y enviarlo en el próximo inicio de la aplicación.

Combinación con sistemas de informes de fallos

Servicios de informes de fallos — Firebase Crashlytics, Sentry, Bugsnag — establecen su propio controlador global. Si un desarrollador configura un controlador personalizado adicional, debe transferir el control al sistema de informes de fallos después de sus propias acciones. En Android se utiliza la composición de controladores: ejecutar la lógica propia y luego llamar al controlador anterior.

Para Firebase Crashlytics, se recomienda no configurar un Thread.setDefaultUncaughtExceptionHandler personalizado, ya que el SDK de Crashlytics lo hace automáticamente al inicializarse.

Registro de información adicional

Contexto del usuario — además de la pila de llamadas estándar, es útil registrar la versión de la aplicación, la versión del SO, el tamaño de memoria disponible y el tiempo de actividad antes del fallo. Estos datos son críticamente importantes para reproducir y solucionar el problema.

En iOS, se puede usar NSSetUncaughtExceptionHandler no solo para escribir, sino también para almacenar datos temporalmente en NSUserDefaults con la bandera synchronize, lo que garantiza su persistencia incluso ante la finalización inmediata del proceso.

Pruebas del controlador antes del lanzamiento

Pruebas obligatorias — el Global Exception Handler debe probarse en cada etapa del CI/CD. En iOS se puede iniciar una excepción de prueba mediante @throw NSException, en Android mediante throw RuntimeException(). Se verifica que el controlador se invoque, que el registro se guarde y que la aplicación finalice correctamente.

Según Google I/O 2023, más del 30% de los fallos en producción ocurren en dispositivos que el desarrollador no probó: diferentes versiones de Android, firmware personalizado, memoria limitada.

Errores típicos al usar el controlador

El primer error y el más común — intentar continuar la ejecución de la aplicación después de manejar una excepción. Después de llamar a uncaughtException, la aplicación se encuentra en un estado inestable, y cualquier operación adicional puede provocar errores en cascada y corrupción de datos.

El segundo error — realizar operaciones prolongadas dentro del controlador. Las solicitudes de red, la escritura de archivos grandes o los cálculos complejos no se completan antes de la finalización forzada del proceso. Según Apple Technical Q&A QA1468, intentar enviar una solicitud HTTP dentro del controlador es la principal causa de pérdida de informes de fallos.

El tercer error — ignorar los hilos en segundo plano. Un Global Exception Handler configurado solo para el hilo principal no protege contra fallos en corrutinas, DispatchQueue, AsyncTask o RxJava. En Android, cada hilo debe tener su propio controlador, y setDefaultUncaughtExceptionHandler solo resuelve esto para hilos sin un controlador individual.

El cuarto error — falta de respaldo para señales del SO. NSSetUncaughtExceptionHandler en iOS no captura SIGABRT, SIGSEGV ni SIGBUS. Estas señales requieren una configuración separada de controladores mediante la API sigaction. Los desarrolladores descubren esto solo cuando la aplicación falla sin un solo informe de fallo.

El quinto error — registro de datos confidenciales. En el registro de fallos pueden aparecer correos electrónicos, tokens de autorización o datos personales de los usuarios. Esto viola el GDPR y las Normas de Revisión de la App Store de Apple. Filtre siempre los datos transmitidos mediante expresiones regulares o una lista blanca de campos permitidos.

Preguntas Frecuentes

¿Se puede restaurar la aplicación después de una excepción global?

No — después de invocar un Global Exception Handler, el estado de la aplicación no está definido. Cualquier intento de continuar la ejecución puede provocar la corrupción de datos. La única acción correcta es guardar el registro del fallo y finalizar el proceso.

¿El Global Exception Handler captura todos los tipos de errores?

No todos — en iOS, NSSetUncaughtExceptionHandler captura solo excepciones de Objective-C. Los errores de Swift y las señales del SO (SIGSEGV, SIGABRT) requieren controladores separados. En Android, Thread.setDefaultUncaughtExceptionHandler captura todas las RuntimeException, pero no los errores de código nativo a través de JNI.

¿Cómo transferir el control a un sistema de informes de fallos después de mi controlador?

Guarde una referencia al controlador anterior mediante Thread.getDefaultUncaughtExceptionHandler() antes de configurar el suyo propio. Al final de su controlador, llame a previousHandler.uncaughtException(thread, throwable) — esto garantiza que Crashlytics o Sentry reciban sus datos.

¿Qué hacer si el fallo ocurre en código nativo C/C++?

Para código nativo, se requiere manejo de señales mediante sigaction() — SIGSEGV, SIGABRT, SIGBUS. En Android se puede usar Google Breakpad o Crashpad. En iOS a partir de la versión 13, está disponible la Signals API para manejar excepciones mach.

¿Puede el Global Exception Handler afectar el rendimiento?

No — la configuración del controlador solo afecta el momento en que ocurre una excepción. En el funcionamiento normal de la aplicación, no hay sobrecarga. El único riesgo es una fuga de memoria si el controlador mantiene una referencia a una Activity o Context, impidiendo la recolección de basura.

Resumen

  • Global Exception Handler — la última línea de defensa antes de un fallo, obligatorio en cualquier aplicación de producción
  • iOS NSSetUncaughtExceptionHandler captura excepciones de Objective-C con un límite de procesamiento de 5 segundos
  • Android Thread.setDefaultUncaughtExceptionHandler funciona para todos los hilos sin un controlador personal
  • Tiempo de ejecución del controlador mínimo — guardar datos y finalizar el proceso sin intentar recuperación
  • Señales del SO (SIGSEGV, SIGABRT) no son capturadas por los controladores estándar — se requiere la API sigaction
  • Sistemas de informes de fallos deben invocarse mediante composición de controladores, transfiriendo el control después de su lógica
  • Pruebas del controlador en CI/CD son obligatorias para evitar la pérdida de informes de fallos en producción

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también