Publiquei a solução simples para tratamento personalizado de travamentos do Android há muito tempo. É um pouco hacky, no entanto, funciona em todas as versões do Android (incluindo o Lollipop).
Primeiro um pouco de teoria. Os principais problemas ao usar o manipulador de exceções não detectadas no Android vêm com as exceções lançadas no thread principal (também conhecido como UI). E aqui está o porquê. Quando o aplicativo é iniciado, o sistema chama o método ActivityThread.main , que prepara e inicia o looper principal do seu aplicativo:
public static void main(String[] args) {
…
…
Looper.prepareMainLooper();
…
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
O looper principal é responsável por processar as mensagens postadas no thread da IU (incluindo todas as mensagens relacionadas à renderização e interação da IU). Se uma exceção for lançada no thread de IU, ela será capturada por seu manipulador de exceções, mas como você está fora do loop()
método, não será capaz de mostrar nenhuma caixa de diálogo ou atividade ao usuário, pois não sobrou ninguém para processar mensagens de IU para voce.
A solução proposta é bastante simples. Executamos o Looper.loop
método por conta própria e o envolvemos com o bloco try-catch. Quando uma exceção é detectada, nós a processamos como queremos (por exemplo, iniciamos nossa atividade de relatório personalizado) e chamamos o Looper.loop
método novamente.
O método a seguir demonstra essa técnica (deve ser chamado a partir do Application.onCreate
ouvinte):
private void startCatcher() {
UncaughtExceptionHandler systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
// the following handler is used to catch exceptions thrown in background threads
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler(new Handler()));
while (true) {
try {
Looper.loop();
Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
throw new RuntimeException("Main thread loop unexpectedly exited");
} catch (Throwable e) {
showCrashDisplayActivity(e);
}
}
}
Como você pode ver, o manipulador de exceções não capturadas é usado apenas para as exceções lançadas em threads de fundo. O manipulador a seguir captura essas exceções e as propaga para o thread de IU:
static class UncaughtHandler implements UncaughtExceptionHandler {
private final Handler mHandler;
UncaughtHandler(Handler handler) {
mHandler = handler;
}
public void uncaughtException(Thread thread, final Throwable e) {
mHandler.post(new Runnable() {
public void run() {
throw new BackgroundException(e);
}
});
}
}
Um exemplo de projeto que usa essa técnica está disponível no meu repositório GitHub: https://github.com/idolon-github/android-crash-catcher