一、實現Thread.UncaughtExceptionHandler
UnChecked異常發生時,由於沒有相應的try…catch處理該異常對象,所以Java運行環境將會終止,程序將退出,也就是我們所說的Crash。Java API提供了一個全局異常捕獲處理器,Android應用在Java層捕獲Crash依賴的就是Thread.UncaughtExceptionHandler處理器接口,通常我們只需實現這個接口,並重寫其中的uncaughtException方法,在該方法中可以讀取Crash的堆棧信息
public class CrashManager implements Thread.UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; private Map<String, String> infos; private MyApplication application; private static SimpleDateFormat logfile = new SimpleDateFormat("yyyy-MM-dd");// 日志文件格式 public CrashManager(MyApplication application){ //獲取系統默認的UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); this.application = application; } private boolean handleException(final Throwable exc){ if (exc == null) { return false; } new Thread(new Runnable() { @Override public void run() { Looper.prepare();//准備 Log.i("Urmytch","崩潰正在寫入日志"); flushBufferedUrlsAndReturn(); //處理崩潰 collectDeviceAndUserInfo(application); writeCrash(exc); Looper.loop(); } }).start(); return true; } /** * 把未存盤的url和返回數據寫入日志文件 */ private void flushBufferedUrlsAndReturn(){ //TODO 可以在請求網絡時把url和返回xml或json數據緩存在隊列中,崩潰時先寫入以便查明原因 } /** * 采集設備和用戶信息 * @param context 上下文 */ private void collectDeviceAndUserInfo(Context context){ PackageManager pm = context.getPackageManager(); infos = new HashMap<String, String>(); try { PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { String versionName = pi.versionName == null?"null":pi.versionName; String versionCode = pi.versionCode + ""; infos.put("versionName",versionName); infos.put("versionCode",versionCode); infos.put("crashTime",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (PackageManager.NameNotFoundException e) { Log.e("Urmytch",e.getMessage()); } Field[] fields = Build.class.getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); infos.put(field.getName(), field.get(null).toString()); } } catch (IllegalAccessException e) { Log.e("Urmytch",e.getMessage()); } } /** * 采集崩潰原因 * @param exc 異常 */ private void writeCrash(Throwable exc){ StringBuffer sb = new StringBuffer(); sb.append("------------------crash----------------------"); sb.append("\r\n"); for (Map.Entry<String,String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key+"="+value+"\r\n"); } Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); exc.printStackTrace(pw); Throwable excCause = exc.getCause(); while (excCause != null) { excCause.printStackTrace(pw); excCause = excCause.getCause(); } pw.close(); String result = writer.toString(); sb.append(result); sb.append("\r\n"); sb.append("-------------------end-----------------------"); sb.append("\r\n"); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String sdcardPath = Environment.getExternalStorageDirectory().getPath(); Log.i("路徑:",""+Environment.getExternalStorageDirectory().getPath()); //String filePath = sdcardPath + "//Urmytch/crash/"; String filePath = sdcardPath + "//kantu/crash/"; writeLog(sb.toString(), filePath); } } /** * * @param log 文件內容 * @param name 文件路徑 * @return 返回寫入的文件路徑 * 寫入Log信息的方法,寫入到SD卡里面 */ private String writeLog(String log, String name) { Date nowtime = new Date(); String needWriteFiel = logfile.format(nowtime); //String filename = name + "mycrash"+ ".log"; String filename = name + "mycrash"+ needWriteFiel+".txt"; File file =new File(filename); if(!file.getParentFile().exists()){ Log.i("Urmytch","新建文件"); file.getParentFile().mkdirs(); } if (file != null && file.exists() && file.length() + log.length() >= 64 * 1024) { //控制日志文件大小 file.delete(); } try { file.createNewFile(); FileWriter fw=new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); //寫入相關Log到文件 bw.write(log); bw.newLine(); bw.close(); fw.close(); //發送郵件 SendMailUtil.send(file,"changyiqiang123@126.com"); return filename; } catch(IOException e) { Log.w("Urmytch",e.getMessage()); return null; } } @Override public void uncaughtException(Thread thread, Throwable exc) { if(!handleException(exc) && mDefaultHandler != null){ //如果用戶沒有處理則讓系統默認的異常處理器來處理 mDefaultHandler.uncaughtException(thread, exc); Log.i("打印:","1111"); }else{ Log.i("打印:","222"); try{ Thread.sleep(2000); }catch (InterruptedException e){ Log.w("Urmytch",e.getMessage()); } Intent intent = new Intent(application.getApplicationContext(), LoginActivity.class); PendingIntent restartIntent = PendingIntent.getActivity(application.getApplicationContext(), 0, intent, 0); //退出程序 AlarmManager mgr = (AlarmManager)application.getSystemService(Context.ALARM_SERVICE); //1秒后重啟應用 mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); android.os.Process.killProcess(android.os.Process.myPid()); } ////這里可以上傳異常信息到服務器,便於開發人員分析日志從而解決Bug // uploadExceptionToServer(); } /** * 將錯誤信息上傳至服務器 */ private void uploadExceptionToServer() { File file = new File(Environment.getExternalStorageDirectory()+File.separator+"test.txt"); OutputStream os = null; try { os = new FileOutputStream(file); String str = "hello world"; byte[] data = str.getBytes(); os.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { if (os != null)os.close(); } catch (IOException e) { } } SendMailUtil.send(file,"changyiqiang123@126.com"); } }
二、在Application中注冊
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); CrashManager crashHandler = new CrashManager(this); Thread.setDefaultUncaughtExceptionHandler(crashHandler); } }
最后權限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
完成
參考於:https://blog.csdn.net/urmytch/article/details/53642945