有時候app會出現崩潰現象咱們沒法徹底控制,而系統的崩潰界面比較差,可能影響用戶體驗,並且咱們須要對崩潰現象進行處理,所以有了這個需求。android
app運行有一個主線程,也就是UI線程,系統奔潰了通常來講是ui線程崩潰了(咱們本身設置的一異步線程,通常本身都會作catch),因此,處理的核心就是 Thread.setDefaultUncaughtExceptionHandler(this);
得到主線程中的設置出現未捕捉的異常的Handler,而後本身從新設置這個handler服務器
本身實現的類CrashHandlr集成UncaughtExceptionHandler,主要經過重載uncaughtException方法來處理異常。
在代碼中,handleException() 經過一個異步線程顯示toast,並進行
收集設備參數信息: collectDeviceInfo(mContext);
保存日誌文件:saveCrashInfo2File(ex);app
代碼以下:異步
/** * 收集手機全局崩潰時的exception,並log到本地 * * @author Jackland_zgl * */ public class CrashHandler implements UncaughtExceptionHandler { public static final String CrashFilePath = "/sdcard/xiaomai/crashlog/"; public static final int LogFileLimit = 10; public static final String TAG = "CrashHandler"; //系統默認的UncaughtException處理類 private Thread.UncaughtExceptionHandler mDefaultHandler; //CrashHandler實例 private static CrashHandler INSTANCE = new CrashHandler(); //程序的Context對象 private Context mContext; //用來存儲設備信息和異常信息 private Map<String, String> infos = new HashMap<String, String>(); //用於格式化日期,做爲日誌文件名的一部分 private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); /** 保證只有一個CrashHandler實例 */ private CrashHandler() { } /** 獲取CrashHandler實例 ,單例模式 */ public static CrashHandler getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; //獲取系統默認的UncaughtException處理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //設置該CrashHandler爲程序的默認處理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 當UncaughtException發生時會轉入該函數來處理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { //若是用戶沒有處理則讓系統默認的異常處理器來處理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(2000); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); } //退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * 收集設備參數信息 * @param ctx */ public void collectDeviceInfo(Context ctx) { try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.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); } } catch (NameNotFoundException e) { Log.e(TAG, "an error occured when collect package info", e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); infos.put(field.getName(), field.get(null).toString()); } catch (Exception e) { Log.e(TAG, "an error occured when collect crash info", e); } } } /** * 保存錯誤信息到文件中 * * @param ex * @return 返回文件名稱,便於將文件傳送到服務器 */ private int saveCrashInfo2File(Throwable ex) { //將設備信息變成string StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\n"); } //遞歸獲取所有的exception信息 Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); sb.append(result); //將寫入的結果 //構造文件名 long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-" + timestamp + ".log"; //寫文件和限制數量 FileUtils.writeFile2SDCard( CrashFilePath,fileName, sb.toString()); cleanLogFileToN(CrashFilePath); return 1; } Comparator<File> newfileFinder = new Comparator<File>(){ @Override public int compare(File x, File y) { // TODO Auto-generated method stub if (x.lastModified()>y.lastModified()) return 1; if (x.lastModified()<y.lastModified()) return -1; else return 0; } }; private int cleanLogFileToN(String dirname){ File dir = new File(dirname); if (dir.isDirectory()){ File[] logFiles = dir.listFiles(); if (logFiles.length>LogFileLimit){ Arrays.sort(logFiles,newfileFinder ); //從小到大排 //刪掉N個之前的 for (int i=0;i<logFiles.length-LogFileLimit;i++){ logFiles[i].delete(); } } } return 1; } /** * 自定義錯誤處理,收集錯誤信息 發送錯誤報告等操做均在此完成. * * @param ex * @return true:若是處理了該異常信息;不然返回false. */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } //使用Toast來顯示異常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "很抱歉,程序出現異常", Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); //收集設備參數信息 collectDeviceInfo(mContext); //保存日誌文件 saveCrashInfo2File(ex); return true; }
}ide
參考: 參考博客1函數
文章爲原創,轉載請註明出處。oop