Android自定義錯誤日誌收集
阿新 • • 發佈:2019-01-02
一、概述
一般做Android開發的朋友多多少少都會碰見各種各樣的問題,一般都怎麼解決這些bug尼?有的朋友會說Debug,但是有沒有想過,萬一客戶上線了尼?打過電話,說軟體出錯了,那這個時候如果不做錯誤收集,那麼就會無法知道發生什麼問題了,這個時候就需要我們自己手動的做錯誤收集了。
如圖:
在第二頁中寫了一個除0的異常,然後進入後發生異常,記錄到日誌並存儲到Sdcard中,最後返回上一頁。
二、自定義我們的異常收集類CrashHandler
** * 自定義異常處理器 * * @author 劉洋巴金 * @date 2017-4-5 * */ public class CrashHandler implements UncaughtExceptionHandler{ private Context mContext; private UncaughtExceptionHandler defaultUncaught; private File logFile = new File(Environment.getExternalStorageDirectory(), "crashLog.trace"); public CrashHandler(Context context) { super(); mContext = context; defaultUncaught = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); // 設定為當前執行緒預設的異常處理器 } }
首先先自定義我們的CrashHandler並實現UncaughtExceptionHandler介面,並保留系統預設異常處理。
然後實現uncaughtException方法
@Override public void uncaughtException(Thread thread, Throwable ex) { // 列印當前的異常資訊 ex.printStackTrace(); // 如果我們沒處理異常,並且系統預設的異常處理器不為空,則交給系統來處理 if(!handlelException(ex) && defaultUncaught != null){ defaultUncaught.uncaughtException(thread, ex); }else{ // 已經記錄完log, 提交伺服器 upLoadErrorFileToServer(logFile); Intent in = new Intent(mContext, MainActivity.class); in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 如果設定了此標誌,這個activity將成為一個新task的歷史堆疊中的第一個activity mContext.startActivity(in); // 殺死我們的程序 Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Process.killProcess(Process.myPid()); // 殺死執行緒 } }, 2 * 1000); } }
Thread.UncaughtExceptionHandler.uncaughtException()會線上程因未捕獲的異常而臨近死亡時被呼叫。
/** * 記錄異常資訊 * */ private boolean handlelException(Throwable ex) { // TODO Auto-generated method stub if(ex == null){ return false; } PrintWriter pw = null; try { if(!logFile.exists()){ logFile.createNewFile(); } pw = new PrintWriter(logFile); // 收集手機及錯誤資訊 collectInfoToSDCard(pw, ex); pw.close(); } catch (Exception e) { e.printStackTrace(); } return true; }
正常的記錄錯誤資訊,把錯誤資訊記錄到手機卡中,如果異常為空,則把許可權交給系統。
/**
* 收集記錄錯誤資訊
* @throws NameNotFoundException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* */
private void collectInfoToSDCard(PrintWriter pw, Throwable ex) throws NameNotFoundException, IllegalAccessException, IllegalArgumentException {
// TODO Auto-generated method stub
PackageManager pm = mContext.getPackageManager();
PackageInfo mPackageInfo = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
pw.println("time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); // 記錄錯誤發生的時間
pw.println("versionCode: " + mPackageInfo.versionCode); // 版本號
pw.println("versionName: " + mPackageInfo.versionName); // 版本名稱
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
pw.print(field.getName() + " : ");
pw.println(field.get(null).toString());
}
ex.printStackTrace(pw);
}
Build類,這個類定義了所有關於手機的一些引數,如版本號,系統名稱,Android版本等。
然後通過反射機制,把這些資訊和錯誤資訊一起記錄到日誌裡面。
getDeclaredFields 獲取所有屬性
field.setAccessible(true); 可以讀取private屬性並可對其進行更改
好了,錯誤資訊都記錄完畢了,最後殺死我們的程序,返回上一級頁面,這樣是為了使用者體驗
// 已經記錄完log, 提交伺服器
upLoadErrorFileToServer(logFile);
Intent in = new Intent(mContext, MainActivity.class);
in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 如果設定了此標誌,這個activity將成為一個新task的歷史堆疊中的第一個activity
mContext.startActivity(in);
// 殺死我們的程序
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Process.killProcess(Process.myPid()); // 殺死執行緒
}
}, 2 * 1000);
當然正常的邏輯,也可以把這個記錄的錯誤日誌發到服務端
好了,最後在自定義的application中使用它吧
public class MyApplication extends Application{
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
new CrashHandler(this);
}
}
完事了
三、一些第三方的錯誤收集
老牌了,可以錯誤收集和檢視渠道。
功能和友盟差不多,支援預警功能,支援“靈動分析”,即動態新增監控事件,目前免費。
鵝廠出品,頁面比友盟好
四、demo