web-dev-qa-db-fra.com

Moyen idéal pour définir le gestionnaire d'exceptions global non capturé dans Android

Je veux définir un gestionnaire d'exceptions global non capturé pour tous les threads dans mon Android. Donc, dans ma sous-classe Application, je définis une implémentation de Thread.UncaughtExceptionHandler comme gestionnaire par défaut pour les exceptions non interceptées.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

Dans mon implémentation, j'essaie d'afficher un AlertDialog affichant un message d'exception approprié.

Cependant, cela ne semble pas fonctionner. Chaque fois qu'une exception est levée pour tout thread qui n'est pas géré, j'obtiens la boîte de dialogue par défaut du système d'exploitation ("Désolé! -La boîte de dialogue Application-a-arrêté-de manière inattendue").

Quelle est la manière correcte et idéale de définir un gestionnaire par défaut pour les exceptions non interceptées?

84
Samuh

Cela devrait être tout ce que vous devez faire. (Assurez-vous d'arrêter le processus par la suite - les choses pourraient être dans un état incertain.)

La première chose à vérifier est de savoir si le gestionnaire Android est toujours appelé. Il est possible que votre version soit appelée mais échoue fatalement et le serveur_système affiche une boîte de dialogue générique lorsqu'il voit le processus se bloquer.

Ajoutez des messages de journal en haut de votre gestionnaire pour voir s'il y parvient. Imprimez le résultat de getDefaultUncaughtExceptionHandler, puis lancez une exception non interceptée pour provoquer un blocage. Gardez un œil sur la sortie logcat pour voir ce qui se passe.

24
fadden

J'ai posté le simple solution pour une gestion personnalisée de Android plante il y a longtemps. C'est un peu hacky mais cela fonctionne sur tous Android versions (y compris la Lollipop).

D'abord un peu de théorie. Les principaux problèmes lorsque vous utilisez le gestionnaire d'exceptions non capturé dans Android viennent avec les exceptions levées dans le thread principal (aka UI). Et voici pourquoi. Lorsque l'application démarre les appels système ActivityThread .main méthode qui prépare et démarre le boucleur principal de votre application:

public static void main(String[] args) {
  …
  …
    Looper.prepareMainLooper();
  …
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Le boucleur principal est responsable du traitement des messages publiés dans le fil d'interface utilisateur (y compris tous les messages liés au rendu et à l'interaction de l'interface utilisateur). Si une exception est levée dans le thread d'interface utilisateur, elle sera interceptée par votre gestionnaire d'exceptions, mais comme vous n'avez plus de méthode loop(), vous ne pourrez pas afficher de boîte de dialogue ou d'activité pour l'utilisateur car il y a plus personne pour traiter les messages de l'interface utilisateur pour vous.

La solution proposée est assez simple. Nous exécutons la méthode Looper.loop Par nos propres moyens et l'entourons d'un bloc try-catch. Lorsqu'une exception est détectée, nous la traitons comme nous le voulons (par exemple, commençons notre activité de rapport personnalisé) et appelons à nouveau la méthode Looper.loop.

La méthode suivante illustre cette technique (elle doit être appelée à partir de l'écouteur Application.onCreate):

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);
        }
    }
}

Comme vous pouvez le voir, le gestionnaire d'exceptions non capturé est utilisé uniquement pour les exceptions levées dans les threads d'arrière-plan. Le gestionnaire suivant intercepte ces exceptions et les propage au thread d'interface utilisateur:

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 exemple de projet utilisant cette technique est disponible sur mon référentiel GitHub: https://github.com/idolon-github/Android-crash-catcher

11
Idolon

FWIW Je sais que c'est légèrement hors sujet, mais nous utilisons le plan gratuit de Crittercism avec succès. Ils offrent également des fonctionnalités premium, comme la gestion de l'exception afin que l'application ne plante pas.

Dans la version gratuite, l'utilisateur voit toujours le plantage, mais au moins je reçois l'e-mail et la trace de la pile.

Nous utilisons également la version iOS (mais mes collègues m'ont dit qu'elle n'était pas aussi bonne).


Voici des questions similaires:

3
Richard Le Mesurier

Je pense que pour désactiver cela dans votre méthode uncaughtException (), n'appelez pas previousHandler.uncaughtException () où previousHandler est défini par

previousHandler = Thread.getDefaultUncaughtExceptionHandler();
3
Robby Pond

Cela ne fonctionne que lorsque vous appelez

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

à la toute fin de votre UncaughtExceptionHandler.

1
Joseph_Marzbani