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


88

Je souhaite définir un gestionnaire d'exceptions global non intercepté pour tous les threads de mon application Android. Donc, dans ma Applicationsous - classe, j'ai défini une implémentation de Thread.UncaughtExceptionHandlercomme gestionnaire par défaut pour les exceptions non interceptées.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

Dans mon implémentation, j'essaye d'afficher un AlertDialogmessage d'exception approprié.

Cependant, cela ne semble pas fonctionner. Chaque fois qu'une exception est lancée pour un thread qui n'est pas géré, j'obtiens la boîte de dialogue stock, par défaut du système d'exploitation ("Désolé! -L'application-s'est-arrêtée-inopinément dialogue").

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


1
Pouvez-vous s'il vous plaît partager le code pour le même ...
Code_Life

2
Si vous souhaitez enregistrer vos exceptions, jetez un oeil à acra.ch . ACRA vous permet d'envoyer des rapports d'erreur à un Google-Doc ou à vous par e-mail.
Alexander Pacha

1
@Alexander Ou vous pouvez simplement utiliser Google Analytics pour Android et enregistrer toutes les exceptions que vous voulez ...
IgorGanapolsky

Réponses:


24

Cela devrait être tout ce que vous avez à faire. (Assurez-vous de provoquer l'arrêt du 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 que 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 plantage. Gardez un œil sur la sortie de logcat pour voir ce qui se passe.


10
«lancer une exception non interceptée pour provoquer un plantage après avoir traité l'erreur» est toujours important. Je viens de le vivre. Mon application a été verrouillée après avoir traité l'exception et n'ai pas lancé d'exception non interceptée.
OneWorld

@OneWorld Jepp pareil ici - au moins pour la partie de verrouillage - il ne semble pas y avoir de moyen de "sauver" l'application du crash après tout.
AgentKnopf

@Zainodis J'ai posté une réponse légèrement hors sujet à cette question en donnant un lien vers Crittercism - je pense qu'ils ont une fonctionnalité qui vous permet de "sauver" l'application du crash après tout. Pas sûr - je n'utilise que la version gratuite atm.
Richard Le Mesurier

@RichardLeMesurier Merci pour l'indice - je vais le vérifier :)!
AgentKnopf

Étrange!!! uncaughtexception appelé même si je gère une exception dans mon activité. Une idée.
Hiren Dabhi

12

J'ai posté la solution simple pour la gestion personnalisée des plantages Android il y a longtemps. C'est un peu hacky mais cela fonctionne sur toutes les versions d'Android (y compris la Lollipop).

D'abord un peu de théorie. Les principaux problèmes lorsque vous utilisez un gestionnaire d'exceptions non interceptées dans Android viennent avec les exceptions levées dans le thread principal (aka UI). Et voici pourquoi. Lorsque l'application démarre, le système appelle la méthode ActivityThread.main 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 de discussion de l'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 de l'interface utilisateur, elle sera interceptée par votre gestionnaire d'exceptions, mais comme vous êtes hors de loop()méthode, vous ne pourrez pas afficher de dialogue ou d'activité à l'utilisateur car il ne reste plus personne pour traiter les messages de l'interface utilisateur. pour toi.

La solution proposée est assez simple. Nous exécutons la Looper.loopméthode par nous-mêmes et l'entourons d'un bloc try-catch. Lorsqu'une exception est interceptée, nous la traitons comme nous le souhaitons (par exemple, nous démarrons notre activité de rapport personnalisé) et appelons à Looper.loopnouveau la méthode.

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

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 interceptées est utilisé uniquement pour les exceptions levées dans les threads d'arrière-plan. Le gestionnaire suivant intercepte ces exceptions et les propage vers le 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


Comme on peut le deviner, de cette façon, il est non seulement possible d'afficher une boîte de dialogue d'erreur personnalisée, mais aussi de permettre à l'utilisateur d'ignorer l'exception et de continuer à travailler avec l'application (et même si cela semble être une mauvaise idée pour les applications publiées, cela pourrait être assez pratique lors d'une session de débogage ou de test).
Idolon

Salut, bien que cela fonctionne pour attraper les exceptions non interceptées, mais, pour une raison quelconque, ce code lève toujours des exceptions. Au départ, je pensais que c'était à cause de la ligne RuntimeException que vous avez dans la méthode startCatcher, mais je reçois toujours une exception après l'avoir supprimée. Je ne sais pas si c'est une chose obligatoire. Quoi qu'il en soit, l'exception que j'obtiens est java.lang.RuntimeException: Performing pause of activity that is not resumed. Je crois que lorsque j'essaye de démarrer mon propre looper, le système met en pause l'activité qui est sur le point de démarrer? Je ne suis pas sûr mais toute aide serait appréciée. Merci
sttaq

aussi si je supprime le startCatcher, c'est-à-dire que je lance l'application sans votre code, tout fonctionne bien sans aucune exception.
sttaq

@sttaq Quelle version d'Android utilisez-vous?
Idolon

Je pense que j'essayais ceci sur 2.3.x
sttaq

3

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

2

FWIW Je sais que c'est un peu hors sujet, mais nous avons utilisé le plan gratuit de Crittercism avec succès. Ils offrent également des fonctionnalités premium, comme la gestion de l'exception pour 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 me disent qu'elle n'est pas aussi bonne).


Voici des questions similaires:


1

Ça ne marche pas tant que tu n'appelles pas

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

à la toute fin de votre UncaughtExceptionHandler.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.