一、为什么要Crash
crash可以理解成堕落,垮台。按照我们通俗理解就是android App 因为不可预知的因素导致奔溃。
即使我们的程序发布前,经历了很多的测试,但是经过无数用户各种使用情况之后,可能会发生意想不到的crash.
为了及时反馈bug,通常我们都需要一个crash机制,以让开发人员尽快了解到问题所在,在下个版本中及时改进。
二、如何做到Crash
java的Thread中有一个UncaughtExceptionHandler接口,该接口的作用主要是为了 当 Thread 因未捕获的异常而突然终止时,调用处理程序。
接口下面有setDefaultUncaughtExceptionHandler(
Thread.UncaughtExceptionHandler eh)
方法,方法主要作用为设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
通用demo如下
1 public class DefaultExceptionHandler implements UncaughtExceptionHandler { 2 3 4 5 private Context act = null; 6 7 8 9 public DefaultExceptionHandler(Context act) { 10 11 this.act = act; 12 13 } 14 15 16 17 @Override 18 19 public void uncaughtException(Thread thread, Throwable ex) { 20 21 22 23 // 收集异常信息 并且发送到服务器 24 25 sendCrashReport(ex); 26 27 28 29 // 等待半秒 30 31 try { 32 33 Thread.sleep(500); 34 35 } catch (InterruptedException e) { 36 37 // 38 39 } 40 41 42 43 // 处理异常 44 45 handleException(); 46 47 48 49 } 50 51 52 53 private void sendCrashReport(Throwable ex) { 54 55 56 57 StringBuffer exceptionStr = new StringBuffer(); 58 59 exceptionStr.append(ex.getMessage()); 60 61 62 63 StackTraceElement[] elements = ex.getStackTrace(); 64 65 for (int i = 0; i < elements.length; i++) { 66 67 exceptionStr.append(elements[i].toString()); 68 69 } 70 71 72 73 //TODO 74 75 //发送收集到的Crash信息到服务器 76 77 } 78 79 80 81 private void handleException() { 82 83 //TODO 84 85 //这里可以对异常进行处理。 86 87 //比如提示用户程序崩溃了。 88 89 //比如记录重要的信息,尝试恢复现场。 90 91 //或者干脆记录重要的信息后,直接杀死程序。 92 93 } 94 95 96 97 }
在主线程中调用
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版本,程序版本,分辨率等等因素