Manera ideal de configurar el controlador de excepciones no detectado global en Android

88

Quiero establecer un controlador de excepciones no detectado global para todos los subprocesos en mi aplicación de Android. Entonces, en mi Applicationsubclase configuré una implementación de Thread.UncaughtExceptionHandlercomo controlador predeterminado para excepciones no detectadas.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

En mi implementación, estoy tratando de mostrar un AlertDialogmensaje de excepción apropiado.

Sin embargo, esto no parece funcionar. Siempre que se lanza una excepción para cualquier hilo que no se maneja, aparece el cuadro de diálogo predeterminado del sistema operativo ("¡Lo siento! -La aplicación se ha detenido inesperadamente").

¿Cuál es la forma correcta e ideal de establecer un controlador predeterminado para las excepciones no detectadas?

Samuh
fuente
1
¿Puede compartir el código para el mismo ...
Code_Life
2
Si desea registrar sus excepciones, echar un vistazo a acra.ch . ACRA le permite enviar informes de errores a un documento de Google o por correo electrónico.
Alexander Pacha
1
@Alexander O simplemente puede usar Google Analytics para Android y registrar todas las excepciones que desee ...
IgorGanapolsky
Esto puede ser de ayuda stackoverflow.com/questions/19897628/…
Aristo Michael

Respuestas:

24

Eso debería ser todo lo que necesita hacer. (Asegúrese de hacer que el proceso se detenga después; las cosas podrían estar en un estado incierto).

Lo primero que hay que comprobar es si todavía se está llamando al controlador de Android. Es posible que su versión esté siendo llamada pero fallando fatalmente y system_server esté mostrando un diálogo genérico cuando ve que el proceso falla.

Agregue algunos mensajes de registro en la parte superior de su controlador para ver si está llegando. Imprima el resultado de getDefaultUncaughtExceptionHandler y luego lance una excepción no detectada para provocar un bloqueo. Esté atento a la salida de logcat para ver qué está pasando.

fadden
fuente
10
"lanzar una excepción no detectada para provocar un bloqueo después de haber manejado el error" sigue siendo importante. Lo acabo de experimentar. Mi aplicación se bloqueó después de que manejé la excepción y no lancé una excepción no detectada.
OneWorld
@OneWorld Jepp es lo mismo aquí, al menos en la parte de bloqueo, parece que no hay forma de "salvar" la aplicación para que no se bloquee después de todo.
AgentKnopf
@Zainodis Publiqué una respuesta ligeramente fuera del tema a esta pregunta dando un enlace a Crittercism - Creo que tienen una función que te permite "salvar" la aplicación para que no se bloquee después de todo. No estoy seguro, solo estoy usando la versión gratuita atm.
Richard Le Mesurier
@RichardLeMesurier Gracias por la pista. ¡Lo comprobaré :)!
AgentKnopf
¡¡¡Extraño!!! uncaughtexception llamado incluso si manejo la excepción en mi actividad. Alguna idea.
Hiren Dabhi
12

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.loopmé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.loopmétodo.

El siguiente método demuestra esta técnica (debe llamarse desde el Application.onCreateoyente):

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 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

Ídolo
fuente
Como se puede suponer, de esta manera no solo es posible mostrar un diálogo de error personalizado, sino también permitir que el usuario ignore la excepción y continúe trabajando con la aplicación (y aunque parece una mala idea para las aplicaciones publicadas, podría ser bastante conveniente durante una sesión de depuración o prueba).
Idolon
Hola, si bien esto funciona para detectar excepciones no detectadas, pero, por alguna razón, este código siempre arroja excepciones. Inicialmente pensé que era debido a la línea RuntimeException que tiene en el método startCatcher, pero todavía obtengo una excepción después de eliminarla. No estoy seguro de si eso es algo obligatorio. De todos modos, la excepción que obtengo es java.lang.RuntimeException: Performing pause of activity that is not resumed. Creo que cuando intento iniciar mi propio bucleador, el sistema detiene la actividad que está a punto de comenzar. No estoy seguro, pero se agradecería cualquier ayuda. Gracias
sttaq
Además, si elimino el startCatcher, es decir, ejecuto la aplicación sin su código, todo funciona bien sin excepciones.
sttaq
@sttaq ¿Qué versión de Android usas?
Idolon
Creo que estaba probando esto en 2.3.x
sttaq
3

Creo que para deshabilitar eso en su método uncaughtException () no llame a previousHandler.uncaughtException () donde previousHandler está configurado por

previousHandler = Thread.getDefaultUncaughtExceptionHandler();
Estanque de Robby
fuente
2

FWIW Sé que esto está un poco fuera de tema, pero hemos estado usando el plan gratuito de Crittercism con éxito. También ofrecen algunas funciones premium, como manejar la excepción para que la aplicación no se bloquee.

En la versión gratuita, el usuario aún ve el bloqueo, pero al menos recibo el correo electrónico y el seguimiento de la pila.

También usamos la versión de iOS (pero mis colegas me han dicho que no es tan buena).


Aquí hay preguntas similares:

Richard Le Mesurier
fuente
1

No funciona hasta que llamas

android.os.Process.killProcess(android.os.Process.myPid());

al final de su UncaughtExceptionHandler.

Joseph_Marzbani
fuente