Ideale Möglichkeit, den globalen Handler für nicht erfasste Ausnahmen in Android festzulegen

87

Ich möchte einen globalen nicht erfassten Ausnahmebehandler für alle Threads in meiner Android-Anwendung festlegen. Daher habe Applicationich in meiner Unterklasse eine Implementierung Thread.UncaughtExceptionHandlerals Standardhandler für nicht erfasste Ausnahmen festgelegt.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

In meiner Implementierung versuche ich, eine AlertDialogentsprechende Ausnahmemeldung anzuzeigen.

Dies scheint jedoch nicht zu funktionieren. Immer wenn eine Ausnahme für einen Thread ausgelöst wird, der nicht behandelt wird, wird der Standarddialog für das Betriebssystem angezeigt ("Entschuldigung! -Anwendung wurde unerwartet gestoppt").

Was ist der richtige und ideale Weg, um einen Standardhandler für nicht erfasste Ausnahmen festzulegen?

Samuh
quelle
1
Können Sie bitte den Code für das gleiche teilen ...
Code_Life
2
Wenn Sie Ihre Ausnahmen protokollieren möchten, schauen Sie sich acra.ch an . Mit ACRA können Sie Fehlerberichte an ein Google-Dokument oder per E-Mail an Sie senden.
Alexander Pacha
1
@ Alexander Oder Sie können einfach Google Analytics für Android verwenden und alle gewünschten Ausnahmen protokollieren ...
IgorGanapolsky
Dies kann helfen stackoverflow.com/questions/19897628/…
Aristo Michael

Antworten:

24

Das sollte alles sein, was Sie tun müssen. (Stellen Sie sicher, dass der Prozess danach angehalten wird - die Dinge könnten sich in einem unsicheren Zustand befinden.)

Als erstes muss überprüft werden, ob der Android-Handler noch aufgerufen wird. Es ist möglich, dass Ihre Version aufgerufen wird, aber tödlich fehlschlägt und der system_server ein generisches Dialogfeld anzeigt, wenn der Prozess abstürzt.

Fügen Sie oben in Ihrem Handler einige Protokollnachrichten hinzu, um festzustellen, ob sie dort ankommen. Drucken Sie das Ergebnis von getDefaultUncaughtExceptionHandler und lösen Sie dann eine nicht erfasste Ausnahme aus, um einen Absturz zu verursachen. Behalten Sie die Logcat-Ausgabe im Auge, um zu sehen, was los ist.

verblassen
quelle
10
"Das Auslösen einer nicht erfassten Ausnahme, um einen Absturz zu verursachen, nachdem Sie den Fehler behoben haben" ist weiterhin wichtig. Ich habe es gerade erlebt. Meine App wurde gesperrt, nachdem ich die Ausnahme behandelt und keine nicht erfasste Ausnahme ausgelöst hatte.
OneWorld
@OneWorld Jepp hier gleich - zumindest für den sperrenden Teil - es scheint keine Möglichkeit zu geben, die App vor einem Absturz zu "retten".
AgentKnopf
@Zainodis Ich habe eine leicht vom Thema abweichende Antwort auf diese Frage gepostet, die einen Link zu Crittercism enthält. Ich denke, sie haben eine Funktion, mit der Sie die App schließlich vor einem Absturz "retten" können. Nicht sicher - ich benutze nur die kostenlose Version atm.
Richard Le Mesurier
@ RichardLeMesurier Danke für den Hinweis - ich werde es überprüfen :)!
AgentKnopf
Seltsam!!! uncaughtexception wird aufgerufen, auch wenn ich mit Ausnahmen in meiner Aktivität umgehe. Irgendeine Idee.
Hiren Dabhi
12

Ich habe vor langer Zeit die einfache Lösung für die benutzerdefinierte Behandlung von Android-Abstürzen veröffentlicht. Es ist ein wenig hackig, funktioniert aber auf allen Android-Versionen (einschließlich Lollipop).

Zuerst ein bisschen Theorie. Die Hauptprobleme bei der Verwendung des nicht erfassten Ausnahmehandlers in Android sind die Ausnahmen, die im Hauptthread (auch als UI-Thread bezeichnet) ausgelöst werden. Und hier ist warum. Wenn die App gestartet wird, ruft das System die ActivityThread.main- Methode auf, die den Main Looper Ihrer App vorbereitet und startet :

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

Der Hauptschleifer ist für die Verarbeitung der im UI-Thread veröffentlichten Nachrichten verantwortlich (einschließlich aller Nachrichten im Zusammenhang mit dem Rendern und der Interaktion der UI). Wenn eine Ausnahme im UI-Thread ausgelöst wird, wird sie von Ihrem Exception-Handler abgefangen. Da Sie loop()jedoch keine Methode mehr haben, können Sie dem Benutzer keine Dialoge oder Aktivitäten anzeigen, da niemand mehr übrig ist, um UI-Nachrichten zu verarbeiten für dich.

Die vorgeschlagene Lösung ist recht einfach. Wir führen die Looper.loopMethode selbst aus und umgeben sie mit einem Try-Catch-Block. Wenn eine Ausnahme abgefangen wird, verarbeiten wir sie wie gewünscht (z. B. starten Sie unsere benutzerdefinierte Berichtsaktivität) und rufen die Looper.loopMethode erneut auf.

Die folgende Methode demonstriert diese Technik (sie sollte vom Application.onCreateListener aufgerufen werden ):

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

Wie Sie sehen können, wird der nicht erfasste Ausnahmebehandler nur für die Ausnahmen verwendet, die in Hintergrundthreads ausgelöst werden. Der folgende Handler fängt diese Ausnahmen ab und gibt sie an den UI-Thread weiter:

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

Ein Beispielprojekt, das diese Technik verwendet, ist auf meinem GitHub-Repo verfügbar: https://github.com/idolon-github/android-crash-catcher

Idolon
quelle
Wie man vermuten könnte, ist es auf diese Weise nicht nur möglich, einen benutzerdefinierten Fehlerdialog anzuzeigen, sondern dem Benutzer auch zu ermöglichen, die Ausnahme zu ignorieren und mit der Anwendung weiterzuarbeiten (und obwohl dies für die veröffentlichten Apps eine schlechte Idee ist, könnte dies der Fall sein ziemlich praktisch während einer Debug- oder Testsitzung).
Idolon
Hallo, dies funktioniert zwar beim Abfangen nicht erfasster Ausnahmen, aber aus irgendeinem Grund löst dieser Code immer Ausnahmen aus. Anfangs dachte ich, es lag an der RuntimeException-Zeile, die Sie in der startCatcher-Methode haben, aber ich erhalte immer noch eine Ausnahme, nachdem ich sie entfernt habe. Ich bin mir nicht sicher, ob das erforderlich ist. Wie auch immer die Ausnahme , dass ich bekommen ist java.lang.RuntimeException: Performing pause of activity that is not resumed. Ich glaube, wenn ich versuche, meinen eigenen Looper zu starten, pausiert das System die Aktivität, die gerade gestartet wird. Ich bin nicht sicher, aber jede Hilfe wäre dankbar. Danke
sttaq
Auch wenn ich den startCatcher entferne, dh die App ohne Ihren Code ausführe, funktioniert alles ohne Ausnahmen einwandfrei.
sttaq
@sttaq Welche Android-Version verwenden Sie?
Idolon
Ich glaube, ich habe dies am 2.3.x
sttaq
3

FWIW Ich weiß, dass dies etwas vom Thema abweicht, aber wir haben den kostenlosen Plan von Crittercism mit Erfolg angewendet . Sie bieten auch einige Premium-Funktionen, wie die Behandlung der Ausnahme, damit die App nicht abstürzt.

In der kostenlosen Version sieht der Benutzer den Absturz immer noch, aber zumindest erhalte ich die E-Mail und den Stack-Trace.

Wir verwenden auch die iOS-Version (aber ich habe von meinen Kollegen gehört, dass es nicht ganz so gut ist).


Hier sind ähnliche Fragen:

Richard Le Mesurier
quelle
3

Ich denke, um zu deaktivieren, dass in Ihrer uncaughtException () -Methode nicht previousHandler.uncaughtException () aufgerufen wird, wobei previousHandler von festgelegt ist

previousHandler = Thread.getDefaultUncaughtExceptionHandler();
Robby Pond
quelle
1

Es funktioniert nicht, bis Sie anrufen

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

ganz am Ende Ihres UncaughtExceptionHandler.

Joseph_Marzbani
quelle