1. 程式人生 > >Android自定義錯誤日誌收集

Android自定義錯誤日誌收集

一、概述

一般做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