Publiqué la solución simple para el manejo personalizado de fallas de Android hace mucho tiempo. Es un poco hacky, sin embargo, funciona en todas las versiones de Android (incluido el Lollipop).
Primero un poco de teoría. Los principales problemas cuando usa un controlador de excepciones no detectado en Android vienen con las excepciones lanzadas en el hilo principal (también conocido como UI). Y he aquí por qué. Cuando la aplicación inicia el sistema llama al método ActivityThread.main que prepara e inicia el bucle principal de su aplicación:
public static void main(String[] args) {
…
…
Looper.prepareMainLooper();
…
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
El looper principal es responsable de procesar los mensajes publicados en el hilo de la interfaz de usuario (incluidos todos los mensajes relacionados con la representación e interacción de la interfaz de usuario). Si se lanza una excepción en el hilo de la interfaz de usuario, su controlador de excepciones la detectará, pero como no tiene loop()
método, no podrá mostrar ningún diálogo o actividad al usuario, ya que no queda nadie para procesar los mensajes de la interfaz de usuario para ti.
La solución propuesta es bastante sencilla. Ejecutamos el Looper.loop
método por nuestra cuenta y lo rodeamos con el bloque try-catch. Cuando se detecta una excepción, la procesamos como queremos (por ejemplo, iniciamos nuestra actividad de informe personalizado) y volvemos a llamar al Looper.loop
método.
El siguiente método demuestra esta técnica (debe llamarse desde el Application.onCreate
oyente):
private void startCatcher() {
UncaughtExceptionHandler systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
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 puede ver, el controlador de excepciones no detectado se usa solo para las excepciones lanzadas en subprocesos en segundo plano. El siguiente controlador detecta esas excepciones y las propaga al hilo de la interfaz de usuario:
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);
}
});
}
}
Un proyecto de ejemplo que utiliza esta técnica está disponible en mi repositorio de GitHub: https://github.com/idolon-github/android-crash-catcher