Ho pubblicato la semplice soluzione per la gestione personalizzata degli arresti anomali di Android molto tempo fa. È un po 'hacky tuttavia funziona su tutte le versioni di Android (incluso il Lollipop).
Prima un po 'di teoria. I problemi principali quando si utilizza un gestore di eccezioni non rilevato in Android vengono con le eccezioni generate nel thread principale (noto anche come interfaccia utente). Ed ecco perché. Quando l'app si avvia, il sistema chiama il metodo ActivityThread.main che prepara e avvia il looper principale della tua app:
public static void main(String[] args) {
…
…
Looper.prepareMainLooper();
…
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Il looper principale è responsabile dell'elaborazione dei messaggi pubblicati nel thread dell'interfaccia utente (inclusi tutti i messaggi relativi al rendering e all'interazione dell'interfaccia utente). Se viene generata un'eccezione nel thread dell'interfaccia utente, verrà rilevata dal gestore delle eccezioni, ma poiché non sei nel loop()
metodo non sarai in grado di mostrare alcuna finestra di dialogo o attività all'utente poiché non è rimasto nessuno per elaborare i messaggi dell'interfaccia utente per te.
La soluzione proposta è abbastanza semplice. Eseguiamo il Looper.loop
nostro metodo e lo circondiamo con il blocco try-catch. Quando viene rilevata un'eccezione, la elaboriamo come desideriamo (ad esempio, avvia la nostra attività di report personalizzato) e richiamiamo Looper.loop
nuovamente il metodo.
Il seguente metodo dimostra questa tecnica (dovrebbe essere chiamata Application.onCreate
dall'ascoltatore):
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);
}
}
}
Come puoi vedere, il gestore delle eccezioni non rilevate viene utilizzato solo per le eccezioni lanciate nei thread in background. Il seguente gestore rileva tali eccezioni e le propaga al thread dell'interfaccia utente:
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 progetto di esempio che utilizza questa tecnica è disponibile sul mio repository GitHub: https://github.com/idolon-github/android-crash-catcher