1. 程式人生 > >收集程式異常資訊

收集程式異常資訊

public class CrashHandler implements UncaughtExceptionHandler
{
    // ** Debug Log Tag *//*
public static final String TAG = "CrashHandler";
    // ** 是否開啟日誌輸出, 在Debug狀態下開啟, 在Release狀態下關閉以提升程式效能 *//*
public static final boolean DEBUG = true;
    // ** CrashHandler例項 *//*
private static CrashHandler INSTANCE;
    // ** 程式的Context物件 *//*
private Context mContext; // ** 系統預設的UncaughtException處理類 *//* private Thread.UncaughtExceptionHandler mDefaultHandler; private static final String VERSION_NAME = "versionName"; private static final String VERSION_CODE = "versionCode"; // ** 錯誤報告檔案的副檔名 *//* private static final String CRASH_REPORTER_EXTENSION
= ".crashlog"; private Application app; // ** 保證只有一個CrashHandler例項 *//* private CrashHandler() { } // ** 獲取CrashHandler例項 ,單例模式 *//* public static CrashHandler getInstance() { if (INSTANCE == null) INSTANCE = new CrashHandler(); return INSTANCE; } /**
* 初始化,註冊Context物件, 獲取系統預設的UncaughtException處理器, 設定該CrashHandler為程式的預設處理器 * * @param ctx */ public void init(Context ctx) { // MobclickAgent.setDebugMode(true); // MobclickAgent.setCatchUncaughtExceptions(true); app = (Application) ctx; mContext = ctx; // mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); sendPreviousReportsToServer(); } /** * 當UncaughtException發生時會轉入該函式來處理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { LogUtil.writeToSDCard("crash.log", LogUtil.exceptionDetail(ex)); // MobclickAgent.reportError(mContext, ex); if (!handleException(ex) && mDefaultHandler != null) { // 如果使用者沒有處理則讓系統預設的異常處理器來處理 mDefaultHandler.uncaughtException(thread, ex); } else { // Sleep一會後結束程式// 來讓執行緒停止一會是為了顯示Toast資訊給使用者,然後Kill程式 try { Thread.sleep(500); } catch (InterruptedException e) { LogUtil.e(TAG, "Error : ", e); } if(LogUtil.DEBUG) { Foreground.exitAllActivity(); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(0); } } } /** * 自定義錯誤處理,收集錯誤資訊 傳送錯誤報告等操作均在此完成. 開發者可以根據自己的情況來自定義異常處理邏輯 * * @param ex * @return true:如果處理了該異常資訊;否則返回false */ private boolean handleException(Throwable ex) { if (ex == null) { return true; } // 收集裝置資訊 提示:收集後,會產生long型別轉換錯誤,需排查原因,如找不到,先註釋掉此行即可。 // 出現long型異常機型:三星i519 // String str = collectCrashDeviceInfo(mContext); StringBuilder sb = new StringBuilder(); sb.append(ServiceManager.getInstance().getIAuthService().getSelfId()); sb.append("\n"); // 儲存錯誤報告檔案 String crashFileName = saveCrashInfoToFile(ex, sb.toString()); // 傳送錯誤報告到伺服器 sendCrashReportsToServer(mContext); return true; } /** * 收集程式崩潰的裝置資訊 * * @param ctx */ public String collectCrashDeviceInfo(Context ctx) { StringBuilder sb = new StringBuilder(); sb.append(ServiceManager.getInstance().getIAuthService().getSelfInfo() .getCellPhone()); sb.append("\n"); try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { sb.append(VERSION_NAME); sb.append(pi.versionName == null ? "not set" : pi.versionName); sb.append("\n"); sb.append(VERSION_CODE); sb.append(pi.versionCode); sb.append("\n"); } } catch (NameNotFoundException e) { LogUtil.e(TAG, "Error while collect package info", e); } // 使用反射來收集裝置資訊.在Build類中包含各種裝置資訊, // 例如: 系統版本號,裝置生產商 等幫助除錯程式的有用資訊 // 返回 Field 物件的一個數組,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { // setAccessible(boolean flag) // 將此物件的 accessible 標誌設定為指示的布林值。 // 通過設定Accessible屬性為true,才能對私有變數進行訪問,不然會得到一個IllegalAccessException的異常 field.setAccessible(true); sb.append(field.getName()); sb.append(field.get(null)); sb.append("\n"); LogUtil.d(TAG, field.getName() + " : " + field.get(null)); } catch (Exception e) { LogUtil.e(TAG, "Error while collect crash info", e); } } return sb.toString(); } /** * 儲存錯誤資訊到檔案中 * * @param ex * @return */ private String saveCrashInfoToFile(Throwable ex, String str) { String result = LogUtil.exceptionDetail(ex); try { SimpleDateFormat sDateFormat = new SimpleDateFormat( "yyyy-MM-dd-hh-mm-ss"); String date = sDateFormat.format(DateUtil.currentTime()); String fileName = "crash-" + date + CRASH_REPORTER_EXTENSION; LogUtil.writeToSDCard(fileName, str + result); return fileName; } catch (Exception e) { LogUtil.e(TAG, "an error occured while writing report file...", e); } return null; } /** * 把錯誤報告發送給伺服器,包含新產生的和以前沒傳送的. * * @param ctx */ private void sendCrashReportsToServer(Context ctx) { if (!DeviceHelper.isNetConnect(mContext)) { return; } File[] crFiles = getCrashReportFiles(); if (crFiles != null && crFiles.length > 0) { for (File f : crFiles) { postReport(f); f.delete();// 刪除已傳送的報告 } } } /** * 獲取錯誤報告檔名 * * @return */ private File[] getCrashReportFiles() { File filesDir = LogUtil.getBaseRoutDir(); // 實現FilenameFilter介面的類例項可用於過濾器檔名 FilenameFilter filter = new FilenameFilter() { // accept(File dir, String name) // 測試指定檔案是否應該包含在某一檔案列表中。 public boolean accept(File dir, String name) { return name.endsWith(CRASH_REPORTER_EXTENSION); } }; // list(FilenameFilter filter) // 返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄 return filesDir.listFiles(filter); } /** * 判斷Android客戶端網路是否連線 * * @param context * @return 真假 */ public static boolean checkNet(Context context) { try { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity != null) { NetworkInfo info = connectivity.getActiveNetworkInfo(); if (info != null && info.isConnected()) { if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } } catch (Exception e) { return false; } return false; } private void postReport(File file) { // file轉成String InputStream isr = null; try { isr = new FileInputStream(file); } catch (FileNotFoundException e1) { LogUtil.d(TAG, "postReport file ", e1); return; } BufferedReader in = new BufferedReader(new InputStreamReader(isr)); StringBuffer buffer = new StringBuffer(); String line = ""; try { while ((line = in.readLine()) != null) { buffer.append(line); buffer.append("\n"); } } catch (IOException e) { LogUtil.d(TAG, "postReport", e); } finally { try { isr.close(); } catch (IOException e) { e.printStackTrace(); } try { in.close(); } catch (IOException e) { e.printStackTrace(); } } // 轉換結果 String end = buffer.toString(); LogUtil.d(TAG, "MobclickAgent postReport send " + end); // MobclickAgent.reportError(mContext, end); } /** * 在程式啟動時候, 可以呼叫該函式來發送以前沒有傳送的報告 */ public void sendPreviousReportsToServer() { sendCrashReportsToServer(mContext); } }