Android应用Crash信息收集

0.前言

每当我们信心满满的写好一个应用放到应用市场的时候,当你拥有一个相当庞大的用户,你会发现各种各样的Bug会出现。当然我们可能会选择友盟提供的SDK去收集和分析这些Bug日志,使用起来非常的方便和简单。当然我们完全可以自己去做一些日志的收集工作。

1.概述

在Android中为我们提供了 UncaughtExceptionHandler接口用来收集未捕捉的Crash信息,只要我们去实现这个接口就可以去对Crash信息进行处理。

2.实现

实现起来时比较简单的,只需要实现UncaughtExceptionHandler接口就行:
package com.wyb.android.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import com.android.common.utils.Logger;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * 收集SDK的Crash信息
 * 当SDK中发生崩溃的时候,我们捕捉到错误进行对应的处理
 * 并且将错误的日志存储在本地文件中
 * 按照一定的规则,择时上传到服务器中
 * 
 * @author Yanbao Wu
 * date: 2014-10-13
 */
public class CrashHandler implements UncaughtExceptionHandler{
	
	public static String phoneId = null;
	public static String TAG = CrashHandler.class.getSimpleName();
	public static String mDeviceName = Build.MODEL.replace(" ", "") ;
	public static String mVersionName = String.valueOf(Build.VERSION.SDK_INT) ;
	
	private Context mContext;
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	private static CrashHandler mInstance = new CrashHandler();
	private Map<String , String> mLogInfo = new HashMap<String , String>() ;
	private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault());
	
	private CrashHandler(){
	}
	
	// 获取实例
	public static CrashHandler getInstance(){
		return mInstance;
	}
	
	/**
	 * 初始化
	 * 
	 * @param context 
	 * 
	 * @author Yanbao Wu
	 * date:2014-10-13
	 */
	public void init(Context context){
		mContext = context;
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	@SuppressWarnings("static-access")
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if ( ! handleException(ex) && mDefaultHandler != null) {
			mDefaultHandler.uncaughtException(thread , ex) ;
		} else {
			try {
				thread.sleep(1000) ;
			}
			catch(InterruptedException e) {
				e.printStackTrace() ;
			}
			android.os.Process.killProcess(android.os.Process.myPid()) ;
			System.exit(1) ;
		}
	}
	
	/**
	 * 处理异常
	 * 
	 * @param paramThrowable
	 * 			需要处理的异常
	 * @return
	 * 			能够处理返回true,否则返回false
	 * 
	 * @author Yanbao wu
	 * date:2014-10-15
	 */
	public boolean handleException(Throwable paramThrowable) {
		if(paramThrowable == null)
			return false ;
		new Thread() {
			public void run() {
				Looper.prepare() ;
				Looper.loop() ;
			}
		}.start() ;
		getDeviceInfo(mContext) ;
		saveCrashLogToFile(paramThrowable);
		return false ;
	}
	
	/**
	 * 获取手机信息
	 * 
	 * @author Yanbao Wu
	 * date:2014-10-15
	 */
	private void getDeviceInfo(Context context){
		
		try {

			PackageManager mPackageManager = context.getPackageManager() ;

			PackageInfo mPackageInfo = mPackageManager.getPackageInfo(
					context.getPackageName() , PackageManager.GET_ACTIVITIES) ;
			if(mPackageInfo != null) {
				String versionName = mPackageInfo.versionName == null ? "null"
						: mPackageInfo.versionName ;
				String versionCode = mPackageInfo.versionCode + "" ;
				mLogInfo.put("versionName" , versionName) ;
				mLogInfo.put("versionCode" , versionCode) ;
			}
		}
		catch(NameNotFoundException e) {
			e.printStackTrace() ;
		}

		Field[] mFields = Build.class.getDeclaredFields() ;
		for(Field field : mFields) {
			try {
				field.setAccessible(true) ;
				mLogInfo.put(field.getName() , field.get("").toString()) ;
				Logger.d(TAG , field.getName() + ":" + field.get("")) ;
			}
			catch(IllegalArgumentException e) {
				e.printStackTrace() ;
			}
			catch(IllegalAccessException e) {
				e.printStackTrace() ;
			}
		}
	}
	
	/**
	 * 保存错误日志到本地
	 * 
	 * @param ex
	 * 			发生的异常
	 * @return 
	 * 			保存成功返回文件名,否则返回null
	 * 
	 * @author Yanbao Wu
	 * date:2014-10-13
	 */
	private String saveCrashLogToFile(Throwable ex){
		StringBuffer mStringBuffer = new StringBuffer() ;
		for(Map.Entry<String , String> entry : mLogInfo.entrySet()) {
			String key = entry.getKey() ;
			String value = entry.getValue() ;
			if (key.equals("TIME")) {
				mStringBuffer.append("TIME="+mSimpleDateFormat.format(new Date()) + "\r\n");
				continue;
			}
			mStringBuffer.append(key + "=" + value + "\r\n") ;
		}
		Writer mWriter = new StringWriter() ;
		PrintWriter mPrintWriter = new PrintWriter(mWriter) ;
		ex.printStackTrace(mPrintWriter) ;
		Throwable mThrowable = ex.getCause() ;
		
		while(mThrowable != null) {
			mThrowable.printStackTrace(mPrintWriter) ;

			mPrintWriter.append("\r\n") ;
			mThrowable = mThrowable.getCause() ;
		}

		mPrintWriter.close() ;
		String mResult = mWriter.toString() ;
		
		// 只收集黄页SDK中的Crash信息
		if (!mResult.contains("superyellowpage")) {
			return null;
		}
		// 将错误的call stack信息追加到日志中
		mStringBuffer.append(mResult) ;

		// 文件名构成
		String mFileName = "wyb_" + mSimpleDateFormat.format(new Date()) + ".log" ;
		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
			try {
				File mDirectory = new File(Environment.getExternalStorageDirectory()
						+ "/.YanbaoCrashLog") ;
				Logger.i(TAG , mDirectory.toString()) ;
				if(!mDirectory.exists())
					mDirectory.mkdir() ;
				
				FileOutputStream mFileOutputStream = new FileOutputStream(mDirectory + "/"
						+ mFileName) ;
				mFileOutputStream.write(mStringBuffer.toString().getBytes()) ;
				mFileOutputStream.close() ;
				return mFileName ;
			}
			catch(FileNotFoundException e) {
				e.printStackTrace() ;
			}
			catch(IOException e) {
				e.printStackTrace() ;
			}
		}
		return null ;
	}
}
如果希望Crash的时候自己去做处理,而不希望让应用蹦掉就可以在 handleException方法中返回true就可以了。
最后需要在你的Application类里面初始化一下:
CrashHandler crashHandler = CrashHandler.getInstance() ;
crashHandler.init(context);


通过以上就可以将用户的Crash信息保存到用户手机的SDcard中,如果有服务端的话,可以择时将文件上传就可以了,这样我们就可以去及早的发现我们的问题进行修复。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值