Cara ideal untuk menyetel Penangan pengecualian tak tertangkap global di Android

88

Saya ingin menyetel pengendali pengecualian tak tertangkap global untuk semua utas dalam aplikasi Android saya. Jadi, di Applicationsubclass saya, saya menetapkan implementasi Thread.UncaughtExceptionHandlersebagai pengendali default untuk pengecualian yang tidak tertangkap.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

Dalam implementasinya, saya mencoba untuk AlertDialogmenampilkan pesan pengecualian yang sesuai.

Namun, ini sepertinya tidak berhasil. Setiap kali, pengecualian dilemparkan untuk utas apa pun yang tidak tertangani, saya mendapatkan stok, dialog OS-default ("Maaf! -Application-has-stop-terduga dialog").

Apa cara yang benar dan ideal untuk menyetel penangan default untuk pengecualian yang tidak tertangkap?

Samuh
sumber
1
Bisakah Anda berbagi kode untuk ...
Code_Life
2
Jika Anda ingin log pengecualian Anda, kita lihat acra.ch . ACRA memungkinkan Anda untuk mengirim laporan kesalahan ke Google-Doc atau kepada Anda melalui E-Mail.
Alexander Pacha
1
@Alexander Atau Anda bisa menggunakan Google Analytics untuk Android, dan mencatat semua pengecualian yang Anda inginkan ...
IgorGanapolsky
Ini mungkin bantuan stackoverflow.com/questions/19897628/…
Aristo Michael

Jawaban:

24

Seharusnya hanya itu yang perlu Anda lakukan. (Pastikan Anda menghentikan proses setelah itu - keadaan bisa jadi tidak pasti.)

Hal pertama yang harus diperiksa adalah apakah penangan Android masih dipanggil. Ada kemungkinan bahwa versi Anda dipanggil tetapi gagal fatal dan system_server menampilkan dialog umum ketika melihat proses macet.

Tambahkan beberapa pesan log di bagian atas penangan Anda untuk melihat apakah itu sampai di sana. Cetak hasil dari getDefaultUncaughtExceptionHandler, lalu lemparkan pengecualian yang tidak tertangkap yang menyebabkan error. Pantau keluaran logcat untuk melihat apa yang terjadi.

iseng
sumber
10
"Melempar pengecualian yang tidak tertangkap untuk menyebabkan kerusakan setelah Anda menangani kesalahan" tetap penting. Saya baru saja mengalaminya. Aplikasi saya terkunci setelah saya menangani pengecualian dan tidak memberikan pengecualian yang tidak tertangkap.
OneWorld
@OneWorld Jepp sama di sini - setidaknya untuk bagian penguncian - tampaknya tidak ada cara untuk "menyelamatkan" aplikasi agar tidak mogok.
AgentKnopf
@Zainodis Saya memposting jawaban agak di luar topik untuk pertanyaan ini yang memberikan tautan ke Kritik - saya pikir mereka memiliki fitur yang memungkinkan Anda untuk "menyelamatkan" aplikasi dari crash. Tidak yakin - Saya hanya menggunakan atm versi gratis.
Richard Le Mesurier
@RichardLeMesurier Terima kasih atas petunjuknya - Saya akan memeriksanya :)!
AgentKnopf
Aneh!!! uncaughtexception dipanggil bahkan jika saya menangani pengecualian dalam aktivitas saya. Ada ide.
Hiren Dabhi
12

Saya memposting solusi sederhana untuk penanganan khusus Android crash sejak lama. Ini sedikit hacky namun bekerja pada semua versi Android (termasuk Lollipop).

Pertama, sedikit teori. Masalah utama saat Anda menggunakan pengendali pengecualian tidak tertangkap di Android datang dengan pengecualian yang dilemparkan di utas utama (alias UI). Dan inilah alasannya. Saat aplikasi memulai sistem memanggil metode ActivityThread.main yang mempersiapkan dan memulai looper utama aplikasi Anda:

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

Looper utama bertanggung jawab untuk memproses pesan yang diposting di thread UI (termasuk semua pesan yang terkait dengan rendering dan interaksi UI). Jika pengecualian dilempar di thread UI, itu akan ditangkap oleh penangan pengecualian Anda, tetapi karena Anda keluar dari loop()metode, Anda tidak akan dapat menampilkan dialog atau aktivitas apa pun kepada pengguna karena tidak ada yang tersisa untuk memproses pesan UI untukmu.

Solusi yang diusulkan cukup sederhana. Kami menjalankan Looper.loopmetode sendiri dan mengelilinginya dengan blok coba-tangkap. Saat pengecualian tertangkap, kami memprosesnya sesuai keinginan (misalnya memulai aktivitas laporan kustom kami) dan memanggil Looper.loopmetode lagi.

Metode berikut mendemonstrasikan teknik ini (harus dipanggil dari Application.onCreatependengar):

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

Seperti yang Anda lihat, penangan pengecualian tak tertangkap hanya digunakan untuk pengecualian yang dilemparkan di utas latar belakang. Penangan berikut menangkap pengecualian tersebut dan menyebarkannya ke UI thread:

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

Contoh proyek yang menggunakan teknik ini tersedia di repo GitHub saya: https://github.com/idolon-github/android-crash-catcher

Idolon
sumber
Seperti yang bisa ditebak, dengan cara ini tidak hanya mungkin untuk menampilkan dialog kesalahan khusus, tetapi juga untuk memungkinkan pengguna mengabaikan pengecualian dan terus bekerja dengan aplikasi (dan meskipun tampaknya ide yang buruk untuk aplikasi yang diterbitkan, itu mungkin saja cukup nyaman selama sesi debug atau pengujian).
Idolon
Hai, meskipun ini berfungsi dalam menangkap pengecualian yang tidak tertangkap tetapi, untuk beberapa alasan, kode ini selalu membuang pengecualian. Awalnya saya pikir itu karena baris RuntimeException yang Anda miliki dalam metode startCatcher tetapi saya masih mendapatkan pengecualian setelah menghapusnya. Saya tidak yakin apakah itu yang diperlukan. Pokoknya pengecualian yang saya dapatkan adalah java.lang.RuntimeException: Performing pause of activity that is not resumed. Saya yakin ketika saya mencoba memulai looper saya sendiri, sistem akan menjeda aktivitas yang akan segera dimulai? Saya tidak yakin tetapi bantuan apa pun akan dihargai. Terima kasih
sttaq
juga jika saya menghapus startCatcher yaitu menjalankan aplikasi tanpa kode Anda maka semuanya bekerja dengan baik tanpa pengecualian.
sttaq
@sttaq Versi Android apa yang Anda gunakan?
Idolon
Saya rasa saya mencoba ini pada 2.3.x
sttaq
3

Saya pikir untuk menonaktifkannya dalam metode uncaughtException () jangan panggil beforeHandler.uncaughtException () di mana beforeHandler ditetapkan oleh

previousHandler = Thread.getDefaultUncaughtExceptionHandler();
Robby Pond
sumber
2

FWIW Saya tahu ini sedikit di luar topik, tetapi kami telah menggunakan paket gratis Crittercism dengan sukses. Mereka juga menawarkan beberapa fitur premium, seperti menangani pengecualian sehingga aplikasi tidak macet.

Dalam versi gratis, pengguna masih melihat kerusakan, tetapi setidaknya saya mendapatkan email dan pelacakan tumpukan.

Kami juga menggunakan versi iOS (tetapi saya telah mendengar dari kolega saya bahwa versi ini kurang bagus).


Berikut pertanyaan serupa:

Richard Le Mesurier
sumber
1

Ini tidak bekerja sampai Anda menelepon

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

di akhir UncaughtExceptionHandler Anda.

Joseph_Marzbani
sumber