一、爲什麼要Crash
crash可以理解成墮落,垮臺。按照我們通俗理解就是android App 因爲不可預知的因素導致奔潰。
即使我們的程序發佈前,經歷了很多的測試,但是經過無數用戶各種使用情況之後,可能會發生意想不到的crash.
爲了及時反饋bug,通常我們都需要一個crash機制,以讓開發人員儘快瞭解到問題所在,在下個版本中及時改進。
二、如何做到Crash
java的Thread中有一個UncaughtExceptionHandler接口,該接口的作用主要是爲了 當 Thread 因未捕獲的異常而突然終止時,調用處理程序。
接口下面有setDefaultUncaughtExceptionHandler(
Thread.UncaughtExceptionHandler eh)
方法,方法主要作用爲設置當線程由於未捕獲到異常而突然終止,並且沒有爲該線程定義其他處理程序時所調用的默認處理程序。
通用demo如下
在主線程中調用
Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler(
this.getApplicationContext()));
之前一直對公司項目的CrashHandler類不是很熟悉,這裏結合項目代碼,看下是如何具體實現的
首先,在AndroidManifest.xml中的application節點中配置name
<application
android:name="com.newland.mbop.application.CrashHandlerApp">
CrashHandlerApp中初始化CrashHandler(實現UncaughtExceptionHandler的實現類)
@Override
public void onCreate() {
CrashHandler ch = CrashHandler.getInstance();
ch.init(this);
super.onCreate();
}
最後看下CrashHandler類的具體實現
public class CrashHandler implements UncaughtExceptionHandler {
/** 獲取CrashHandler實例 */
public static CrashHandler getInstance() {
if (INSTANCE == null)
INSTANCE = new CrashHandler();
return INSTANCE;
}
public void init(CrashHandlerApp app) {
Log.i("BaseActivity","init()");
this.app = app;
// 設置該類爲線程默認UncatchException的處理器。
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 當UncaughtException發生時會回調該函數來處理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
System.out.println("system wrong....");
// MBOPApplication app=(MBOPApplication) mainContext;
// app.setNeed2Exit(true);
//異常信息收集
collectCrashExceptionInfo(thread, ex);
//應用程序信息收集
collectCrashApplicationInfo(app);
//保存錯誤報告文件到文件。
saveCrashInfoToFile(ex);
//MBOPApplication.setCrash(true);
//判斷是否爲UI線程異常,thread.getId()==1 爲UI線程
if (thread.getId() != 1) {
// System.out.println("Exception ThreadId" + thread.getId());
thread.interrupt();
//TODO 跳轉到IndexActivity
System.out.println("Thread ID--->" + Thread.currentThread().getId());
// Intent intent =new Intent(mainContext,IndexActivity.class);
// actContext.startActivity(intent);
//彈出對話框提示用戶是否上傳異常日誌至服務器
new Thread(){
public void run() {}{
Looper.prepare();
new AlertDialog.Builder(app.getCurrentAct()).setTitle("異常處理").setMessage("您的程序出現異常,是否將異常信息上傳至服務器?")
.setPositiveButton("是", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
new Thread(new Runnable() {
@Override
public void run() {
sendCrashReportsToServer(app,false);
}
}).start();
// new Thread(){
// public void run() {}{
// try{
// System.out.println("執行上傳線程ID"+this.getId());
// this.sleep(5000);
// }catch(Exception e){
//
// }
// sendCrashReportsToServer(app);
// }
// }.start();
}
}).setNegativeButton("否", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).create().show();
Looper.loop();
}
}.start();
} else {
// UserSessionCache usc=UserSessionCache.getInstance();
// ObjectOutputStream oos=null;
// try {
// oos.writeObject(usc);
// } catch (IOException e) {
// e.printStackTrace();
// }
// SharedPreferences prefenPreferences = mainContext
// .getSharedPreferences("IsMBOPCrash",Activity.MODE_PRIVATE);
// SharedPreferences.Editor editor = prefenPreferences.edit();
// editor.clear();
// editor.putBoolean("ISCRASH", true);
// editor.commit();
// 方案一:將所有Activity放入Activity列表中,然後循環從列表中刪除,即可退出程序
for (int i = app.getActivityList().size()-1; i >=0; i--) {
Activity act = app.getActivityList().get(i);
act.finish();
}
CoreCommonMethod.setCrash(app, true);
Intent intent = new Intent(app, WelcomeActivity.class);
intent.putExtra(WelcomeActivity.EXTRA_DIRECT_TO_INDEX, true);
intent.putExtra(WelcomeActivity.EXTRA_USERINFO, UserSessionCache.getInstance().getUserInfo());
intent.putExtra(WelcomeActivity.EXTRA_CURRENT_PORTAL_ID, UserSessionCache.getInstance().getCurrentPortalId());
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// app.startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
//方案二:直接使用ActivityManager的restartPackage方法關閉應用程序,
//此方法在android2.1之後被棄用,不起作用
// ActivityManager am = (ActivityManager) mainContext.getSystemService(Context.ACTIVITY_SERVICE);
// am.restartPackage(mainContext.getPackageName());
}
}
}
一般來說,發生crash的時候,我們需要知道客戶端的SDK版本,程序版本,分辨率等等因素