一、概述
一般做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);
}
}
完事了
三、一些第三方的错误收集
1. 友盟错误统计
老牌了,可以错误收集和查看渠道。
2. TalkingData
功能和友盟差不多,支持预警功能,支持“灵动分析”,即动态添加监控事件,目前免费。
3. bugly
鹅厂出品,页面比友盟好
四、demo
Android崩溃日志收集