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中,如果有服务端的话,可以择时将文件上传就可以了,这样我们就可以去及早的发现我们的问题进行修复。