每個(gè)應(yīng)用在所難免會(huì)有bug,崩潰的時(shí)候一閃而過(guò),這樣的話交互非常的差。在此,我為大家介紹一種捕捉崩潰信息,并且友好的提示用戶的方式。
在Thread類中有這樣一個(gè)函數(shù)
/**
* Set the default handler invoked when a thread abruptly terminates
* due to an uncaught exception, and no other handler has been defined
* for that thread.
*
* <p>Uncaught exception handling is controlled first by the thread, then
* by the thread's {@link ThreadGroup} object and finally by the default
* uncaught exception handler. If the thread does not have an explicit
* uncaught exception handler set, and the thread's thread group
* (including parent thread groups) does not specialize its
* <tt>uncaughtException</tt> method, then the default handler's
* <tt>uncaughtException</tt> method will be invoked.
* <p>By setting the default uncaught exception handler, an application
* can change the way in which uncaught exceptions are handled (such as
* logging to a specific device, or file) for those threads that would
* already accept whatever "default" behavior the system
* provided.
*
* <p>Note that the default uncaught exception handler should not usually
* defer to the thread's <tt>ThreadGroup</tt> object, as that could cause
* infinite recursion.
*
* @param eh the object to use as the default uncaught exception handler.
* If <tt>null</tt> then there is no default handler.
*
* @throws SecurityException if a security manager is present and it
* denies <tt>{@link RuntimePermission}
* ("setDefaultUncaughtExceptionHandler")</tt>
*
* @see #setUncaughtExceptionHandler
* @see #getUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
* @since 1.5
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
defaultUncaughtExceptionHandler = eh;
}
通過(guò)注釋我們可以看出這個(gè)函數(shù)是用于設(shè)置當(dāng)線程由于未捕獲的異常突然終止而調(diào)用的默認(rèn)處理程序,下面我們通過(guò)代碼來(lái)熟知這個(gè)函數(shù)的作用。
我們先定義一個(gè)CrashHandler類,這個(gè)類實(shí)現(xiàn)Thread.UncaughtExceptionHandler接口,重寫uncaughtException方法來(lái)捕捉未處理的異常信息并通過(guò)Log打印出來(lái)。代碼如下:
package com.zzw.TestCrashHandler;
import android.util.Log;
/**
* Created by zzw on 2017/4/28.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "CrashHandler";
private static CrashHandler instance;
private CrashHandler() {
}
public static CrashHandler getInstance() {
if (instance == null) {
instance = new CrashHandler();
}
return instance;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.e(TAG, e.toString());
}
public void init() {
Thread.setDefaultUncaughtExceptionHandler(this);
}
}
接下來(lái)我們?cè)?code>Application的onCreate()里面初始化:
package com.zzw.TestCrashHandler;
import android.app.Application;
/**
* Created by zzw on 2017/4/28.
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().init();
}
}
我們接著制造一個(gè)bug出來(lái):
package com.zzw.TestCrashHandler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.crash_bt).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.crash_bt:
String nullStr = null;
Log.e("zzz", nullStr);
break;
}
}
}

image.png
異常捕捉到了,但是卻卡主了,然后就是ANR,看來(lái)是造成線程堵塞了,所以我們必須把這個(gè)給處理了。
package com.zzw.TestCrashHandler;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
/**
* Created by zzw on 2017/4/28.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "CrashHandler";
private static CrashHandler instance;
private Thread.UncaughtExceptionHandler defaultHandler;//系統(tǒng)默認(rèn)的UncaughtException處理器
private Context context;
private CrashHandler() {
}
public static CrashHandler getInstance() {
if (instance == null) {
instance = new CrashHandler();
}
return instance;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!handleException(e) && defaultHandler != null) { //如果用戶沒(méi)有處理則讓系統(tǒng)默認(rèn)的異常處理器來(lái)處理
defaultHandler.uncaughtException(t, e);
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Log.e(TAG, "error : ", ex);
}
//退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
public void init(Context context) {
this.context = context;
//獲取系統(tǒng)默認(rèn)的UncaughtException處理器
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
private boolean handleException(Throwable e) {
if (e == null)
return false;
//使用Toast來(lái)顯示異常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, "程序發(fā)生意外情況,即將關(guān)閉,我們深感抱歉!我們將會(huì)盡快修復(fù)!", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
saveException(e);
return true;
}
/**
* 一般在這是把崩潰信息保存下來(lái),然后等wifi的時(shí)候上傳到服務(wù)器
*
* @param e 異常信息
*/
private void saveException(Throwable e) {
Log.e(TAG, e.toString());
}
}
這樣的話我們就能夠提示用戶,然后把異常信息保存下來(lái),等待有Wifi的時(shí)候上傳就可以了。
效果如下:

這篇文章到這,Demo就不上傳了,希望大家有所收獲,有什么建議在下方評(píng)論,謝謝。