Global Exception Handler — um mecanismo centralizado para capturar exceções não tratadas que evita o encerramento abrupto de aplicativos móveis. De acordo com Apple Developer, 2024, o tratamento adequado de exceções reduz a quantidade de falhas em 40–60% e melhora a experiência do usuário. Sem esse manipulador, qualquer exceção não tratada em uma thread em segundo plano leva ao fechamento imediato do aplicativo.
Pontos Principais
Global Exception Handler — é um mecanismo centralizado para capturar exceções que não foram tratadas no nível de funções ou módulos individuais do aplicativo. No contexto do desenvolvimento móvel, esse manipulador atua como a última linha de defesa antes do encerramento anormal do processo.
iOS e Android fornecem APIs integradas para definir um manipulador global. A Apple usa NSSetUncaughtExceptionHandler para o ambiente Objective-C, enquanto o Google oferece Thread.setDefaultUncaughtExceptionHandler em Java/Kotlin. Ambos os mecanismos capturam exceções que não foram pegas por construções try-catch em todas as threads do aplicativo.
De acordo com o Crashlytics (Google, 2024), cerca de 25% das falhas ocorrem devido a exceções não tratadas em threads em segundo plano — uma área onde o Global Exception Handler é especialmente crítico. Os desenvolvedores geralmente se concentram na thread de UI, esquecendo das operações assíncronas.
Usar um manipulador global não substitui o tratamento local de erros, mas o complementa. A principal tarefa é salvar o máximo de informações sobre o estado do aplicativo no momento da exceção e encerrar corretamente.
O mecanismo de funcionamento do Global Exception Handler é baseado na interceptação de sinais do sistema operacional ou exceções em tempo de execução. Quando o código lança uma exceção que não é capturada por nenhum bloco try-catch, o controle é transferido para um manipulador previamente registrado.
No iOS, o manipulador é registrado via NSSetUncaughtExceptionHandler e recebe um objeto NSException com um stack trace completo. No Android, usa-se Thread.setDefaultUncaughtExceptionHandler, que aceita Thread e Throwable — fornecendo acesso ao tipo de exceção, mensagem e pilha de chamadas.
Após receber os dados da falha, o manipulador executa três ações obrigatórias: gravar um log no armazenamento local, enviar um relatório para o Crashlytics ou Sentry e encerrar o aplicativo corretamente. De acordo com a Apple WWDC 2023, o tempo de execução do manipulador é limitado a 5 segundos — após isso, o sistema encerra o processo à força.
Para aplicativos Swift a partir do iOS 13, foi introduzida a Signals API, que lida não apenas com exceções, mas também com sinais do sistema operacional — SIGABRT, SIGSEGV e SIGBUS, estendendo a cobertura do manipulador para erros de memória de baixo nível.
A implementação de um manipulador global no iOS requer a configuração de uma função C via NSSetUncaughtExceptionHandler. O manipulador é chamado de forma síncrona no momento de uma exceção não tratada e recebe o contexto completo do erro.
void handleUncaughtException(NSException exception) {
NSDictionary userInfo = [exception userInfo];
NSArray stackTrace = [exception callStackSymbols];
NSString reason = [exception reason];
// Salvar log de crash em arquivo 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);
}
Uma característica importante da implementação no iOS: o manipulador captura apenas exceções Objective-C. Erros Swift que usam o mecanismo throw-catch não chegam a este manipulador — eles exigem tratamento separado via Swift Error Handling. A partir do iOS 14, a Apple recomenda combinar NSSetUncaughtExceptionHandler com a Signals API para máxima cobertura.
De acordo com Apple Technical Note TN2151, após a chamada do manipulador, o aplicativo deve encerrar em 5 segundos. Qualquer tentativa de continuar a execução após retornar do manipulador leva a comportamento indefinido e uma nova falha.
O Android fornece um mecanismo mais flexível para tratamento global de exceções via Thread.setDefaultUncaughtExceptionHandler. O manipulador recebe uma referência à thread onde ocorreu a exceção e o próprio objeto Throwable.
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, throwable: Throwable) {
// Salvar log de crash em arquivo
val stackTrace = throwable.stackTraceToString()
val crashLog = "CRASH: ${thread.name}\n${stackTrace}"
val file = File(context.cacheDir, "crash_log.txt")
file.writeText(crashLog)
// Enviar para Crashlytics
FirebaseCrashlytics.getInstance()
.recordException(throwable)
// Encerrar processo
android.os.Process.killProcess(
android.os.Process.myPid()
)
}
}
// Configuração no Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(
GlobalExceptionHandler()
)
}
}
Uma diferença chave da implementação no Android: cada thread tem seu próprio manipulador, e setDefaultUncaughtExceptionHandler define o manipulador para todas as threads que não têm um manipulador individual atribuído. Isso garante cobertura global — da thread de UI às AsyncTask em segundo plano e corrotinas.
No Android 12+, há uma limitação: após chamar uncaughtException, o aplicativo deve encerrar em 100 milissegundos. Se o manipulador realizar operações demoradas, o sistema pode matar o processo antes que o log seja gravado. Recomenda-se usar um serviço em segundo plano para enviar relatórios de falha.
A primeira regra — não tente restaurar a operação do aplicativo após uma exceção não tratada. O estado do aplicativo após uma falha é indefinido, e continuar a execução pode levar à corrupção dos dados do usuário.
Limite de tempo — a principal restrição técnica do Global Exception Handler. No iOS é de 5 segundos, no Android — 100 milissegundos. Dentro do manipulador, apenas um conjunto mínimo de dados deve ser salvo: tipo de exceção, pilha de chamadas e o estado de algumas variáveis-chave.
O envio de solicitações de rede, gravação em banco de dados e serialização complexa devem ser adiados para um mecanismo diferido — por exemplo, salvar o log em um arquivo e enviá-lo na próxima inicialização do aplicativo.
Serviços de relatório de falhas — Firebase Crashlytics, Sentry, Bugsnag — definem seu próprio manipulador global. Se um desenvolvedor definir um manipulador personalizado adicional, ele precisa passar o controle para o sistema de relatório de falhas após suas próprias ações. No Android, usa-se composição de manipuladores: execute sua lógica e depois chame o manipulador anterior.
Para Firebase Crashlytics, é recomendado não definir um Thread.setDefaultUncaughtExceptionHandler personalizado — o SDK do Crashlytics faz isso automaticamente na inicialização.
Contexto do usuário — além da pilha de chamadas padrão, é útil registrar a versão do aplicativo, versão do SO, tamanho da memória disponível e tempo de atividade antes da falha. Esses dados são criticamente importantes para reproduzir e corrigir o problema.
No iOS, pode-se usar NSSetUncaughtExceptionHandler não apenas para gravar, mas também para armazenamento temporário em NSUserDefaults com o sinalizador synchronize — isso garante a persistência mesmo no encerramento imediato do processo.
Teste obrigatório — o Global Exception Handler deve ser testado em cada etapa do CI/CD. No iOS, uma exceção de teste pode ser acionada via @throw NSException, no Android — via throw RuntimeException(). Verifique se o manipulador é chamado, o log é salvo e o aplicativo encerra corretamente.
De acordo com Google I/O 2023, mais de 30% das falhas em produção ocorrem em dispositivos que o desenvolvedor não testou — diferentes versões do Android, firmware personalizado, memória limitada.
O primeiro e mais comum erro — tentar continuar a execução do aplicativo após tratar uma exceção. Depois de chamar uncaughtException, o aplicativo está em um estado instável, e qualquer operação adicional pode causar erros em cascata e corrupção de dados.
O segundo erro — realizar operações demoradas dentro do manipulador. Solicitações de rede, gravação de arquivos grandes ou cálculos complexos não são concluídos antes do encerramento forçado do processo. De acordo com Apple Technical Q&A QA1468, tentar enviar uma solicitação HTTP dentro do manipulador é a principal causa de perda de relatórios de falha.
O terceiro erro — ignorar threads em segundo plano. Um Global Exception Handler definido apenas para a thread principal não protege contra falhas em corrotinas, DispatchQueue, AsyncTask ou RxJava. No Android, cada thread deve ter seu próprio manipulador — e setDefaultUncaughtExceptionHandler só resolve isso para threads sem um manipulador individual.
O quarto erro — falta de fallback para sinais do SO. NSSetUncaughtExceptionHandler no iOS não captura SIGABRT, SIGSEGV e SIGBUS. Esses sinais exigem configuração separada de manipuladores via sigaction API. Os desenvolvedores descobrem isso apenas quando o aplicativo falha sem um único relatório de falha.
O quinto erro — registro de dados confidenciais. O log de falha pode conter e-mails, tokens de autenticação ou dados pessoais dos usuários. Isso viola o GDPR e as Diretrizes de Revisão da App Store da Apple. Sempre filtre os dados transmitidos usando regex ou uma lista de campos permitidos.
Perguntas Frequentes
Não — após invocar um Global Exception Handler, o estado do aplicativo é indefinido. Qualquer tentativa de continuar a execução pode levar à corrupção de dados. A única ação correta é salvar o log da falha e encerrar o processo.
Nem todos — no iOS, NSSetUncaughtExceptionHandler captura apenas exceções Objective-C. Erros Swift e sinais do SO (SIGSEGV, SIGABRT) exigem manipuladores separados. No Android, Thread.setDefaultUncaughtExceptionHandler captura todas as RuntimeExceptions, mas não erros de código nativo via JNI.
Salve uma referência ao manipulador anterior via Thread.getDefaultUncaughtExceptionHandler() antes de definir o seu. No final do seu manipulador, chame previousHandler.uncaughtException(thread, throwable) — isso garante que o Crashlytics ou Sentry recebam seus dados.
Para código nativo, é necessário tratamento de sinais via sigaction() — SIGSEGV, SIGABRT, SIGBUS. No Android, pode-se usar Google Breakpad ou Crashpad. No iOS a partir da versão 13, a Signals API está disponível para lidar com exceções mach.
Não — definir o manipulador afeta apenas o momento em que uma exceção ocorre. Na operação normal do aplicativo, não há overhead. O único risco é um vazamento de memória se o manipulador mantiver uma referência a uma Activity ou Context, impedindo a coleta de lixo.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também