每個應用在所難免會有bug
,崩潰的時候一閃而過,這樣的話交互非常的差。在此,我為大家介紹一種捕捉崩潰信息,并且友好的提示用戶的方式。
在Thread
類中有這樣一個函數
/**
* 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;
}
通過注釋我們可以看出這個函數是用于設置當線程由于未捕獲的異常突然終止而調用的默認處理程序,下面我們通過代碼來熟知這個函數的作用。
我們先定義一個CrashHandler
類,這個類實現Thread.UncaughtExceptionHandler
接口,重寫uncaughtException
方法來捕捉未處理的異常信息并通過Log
打印出來。代碼如下:
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);
}
}
接下來我們在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();
}
}
我們接著制造一個bug
出來:
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,看來是造成線程堵塞了,所以我們必須把這個給處理了。
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;//系統默認的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) { //如果用戶沒有處理則讓系統默認的異常處理器來處理
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;
//獲取系統默認的UncaughtException處理器
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
private boolean handleException(Throwable e) {
if (e == null)
return false;
//使用Toast來顯示異常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, "程序發生意外情況,即將關閉,我們深感抱歉!我們將會盡快修復!", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
saveException(e);
return true;
}
/**
* 一般在這是把崩潰信息保存下來,然后等wifi的時候上傳到服務器
*
* @param e 異常信息
*/
private void saveException(Throwable e) {
Log.e(TAG, e.toString());
}
}
這樣的話我們就能夠提示用戶,然后把異常信息保存下來,等待有Wifi的時候上傳就可以了。
效果如下:
這篇文章到這,Demo就不上傳了,希望大家有所收獲,有什么建議在下方評論,謝謝。