android崩溃日志收集和处理

2,931 阅读7分钟

    android的崩溃很常见,我们往往通过日志收集避免下次更新的重复出错。

    android的崩溃发生后通常是由:

Thread.UncaughtExceptionHandler

这个类进行处理的,那么我们要收集日志就要继承这个类进行一些处理即可。

当我们继承后,有个方法必须要我们重写,那就是:

@Override
public void uncaughtException(Thread thread, Throwable throwable) {
}

    当崩溃发生时,这个类就会调用这个方法进行处理,默认处理是手机卡住,然后几个键失灵,然后出现崩溃或者程序无响应对话框告诉你程序已经崩溃。

    但是现在我们要做处理了,我这里用到了三个类:

1:ErrorCaught 继承刚才说的类,也就是Thread.UncaughtExceptionHandler

2:ErrorHandle 看单词就知道,错误处理,那么当上面的错误发生后,一些操作是在这里面进行的,可能包含错误上传,保存这样的

3:CrashInforMationDetail 这个看单词也容易知道意思,具体的错误信息,意思就是我们手机的错误日志就要从这里面拿

好了,分配好任务,下面开始具体的操作

按顺序来,先说第一个类:ErrorCaught, 继承了Thread.UncaughtExceptionHandler,那重点自然是看uncaughtException这个方法的具体操作,贴出代码:

//异常崩溃发生时调用的方法,这里面开始我们想要的操作,包括日志的手机和上传等

@Override
public void uncaughtException(Thread thread, Throwable throwable) {
errHandle.excute(thread, throwable);
}

    出现了一个errHandle,不认得?不要紧,这就是我们上面说的第二个类,那么这里我们可以看到就是当崩溃发生时,我们调用了第二个类的一个方法,但是我们看到这里没有给errHandle初始化,那我们再看看第一个类:ErrorCaught的构造器

private ErrorHandleerrHandle;
//设置本程序的异常崩溃由此类处理
public ErrorCaught(Application context){
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
errHandle =new ErrorHandle(context , uncaughtExceptionHandler);
Thread.setDefaultUncaughtExceptionHandler(this);
}

    很简单的一个构造器,就传了一个Context,但是有两行代码看不懂,中间的第二个类用构造器初始化大家应该看的懂吧,那么就说

Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);

    这俩行的意思就是设置当前程序的崩溃类为这个类,也就是把原本系统捕捉崩溃的类的工作给代替了,这里我们是要写上的。

    那这第一个类的重点就很明显了,就是errHandle.excute(thread, throwable),我们去看看这个所谓的错误处理的类,到底是怎么处理崩溃这个事情的,打开这个类,我们看我们excute这个方法干了啥,

//用来执行崩溃时具体的操作

public void excute(Thread thread, Throwable throwable) {
CrashInforMationDetail crashInforMationDetail = CrashInforMationDetail.produce(throwable, thread,context);
crashInforMationDetail.writeToFile(crashFile);
signOut(thread, throwable);
}

    厉害了哈,这么快我们的第三个类就登场了,从代码中我们大概能看出,初始化了我们的第三个类,然后调用了writeToFile这个方法,很明显crashFile是一个文件,那这里的意思就是把错误的信息写到一个文件里面的,最后的signOut(thread, throwable);也容易看出来,退出程序,那思路就很明确了,当程序崩溃时,我们调用了我们捕捉崩溃的类,然后在捕捉崩溃的方法里面做了两件事,一:是保存错误信息,二:推出程序,和我们平时崩溃的不一样的就在通常程序崩溃了就没了,但是我们的不一样,就是做了日志保存处理,嗯,很智能。

    接着说第二个类吧,这个signOut方法是退出程序,我们看看代码:

//强制退出软件

public void signOut(Thread thread, Throwable throwable) {
if (uncaughtExceptionHandler !=null) {
uncaughtExceptionHandler.uncaughtException(thread, throwable);
}else {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}

    又发现了我们第一个类的身影,它在这干啥,我们加了一个判断如果第一个类没有捕捉到崩溃异常处理,我们这里就把这个异常又交给系统去,如果获取本地崩溃的捕捉类,那就我们自己处理。还记得我们第一个类的构造器怎么写的么?里面有这俩行代码:

Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
errHandle =new ErrorHandle(context , uncaughtExceptionHandler);
 看到没,我们第二个类获取的uncaughtExceptionHandler,是从第一个类来的,所以这里的处理就是没有一就没有二的意思。
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
    这俩行代码的意思就杀掉进程,退出程序。
    我们再看第一个类的构造器:
public ErrorHandle(Application context, Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
this.context = context;
this.uncaughtExceptionHandler = uncaughtExceptionHandler;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
File file =new File(Environment.getExternalStorageDirectory(),"crashCollection");
if (!file.exists()) {
file.mkdirs();//创建崩溃捕捉所在文件夹
        }
crashFile =new File(file, getCrashFileName());
if (!crashFile.exists()) {
try {
crashFile.createNewFile();//创建崩溃捕捉文件
            }catch (IOException e) {
e.printStackTrace();
}
}
}
}

    意图大概很明确,是一个创建文件夹和文件的过程,那肯定就是创建崩溃日志的收集文件夹和文件。当然啦,我们要先检查有没有去权限等操作。看这个:getCrashFileName(),我们没找到是啥是吧,就是确定我们文件名的方法,给出代码:

//获取崩溃文件名称,具体是年月日组成的文件名

private String getCrashFileName() {
StringBuilder stringBuilder =new StringBuilder();
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int date = calendar.get(Calendar.DATE);
stringBuilder.append("crash_");
stringBuilder.append(year +"-");
stringBuilder.append(month +"-");
stringBuilder.append(date);
stringBuilder.append(".txt");
return stringBuilder.toString();
}
     下面说第三个类
CrashInforMationDetail    用来采集错误信息的类,那么这里就是需要根据不同的需求来进行获取不同的信息,但是一般会包括俩种信息,
1:手机设备的信息;2:崩溃产生的错误信息
根据第一个错我这里写的有如下:
//获取手机的一些设备参数
public static String getSysytemInfor() {
StringBuffer sb =new StringBuffer();
sb.append("主板:" +Build.BOARD +"\n");
sb.append("系统启动程序版本号:" +Build.BOOTLOADER +"\n");
sb.append("系统定制商:" +Build.BRAND +"\n");
sb.append("cpu指令集:" +Build.CPU_ABI +"\n");
sb.append("cpu指令集2:" +Build.CPU_ABI2 +"\n");
sb.append("设置参数:" +Build.DEVICE +"\n");
sb.append("显示屏参数:" +Build.DISPLAY +"\n");
sb.append("无线电固件版本:" +Build.getRadioVersion() +"\n");
sb.append("硬件识别码:" +Build.FINGERPRINT +"\n");
sb.append("硬件名称:" +Build.HARDWARE +"\n");
sb.append("HOST:" +Build.HOST +"\n");
sb.append("修订版本列表:" +Build.ID +"\n");
sb.append("硬件制造商:" +Build.MANUFACTURER +"\n");
sb.append("版本:" +Build.MODEL +"\n");
sb.append("硬件序列号:" +Build.SERIAL +"\n");
sb.append("手机制造商:" +Build.PRODUCT +"\n");
sb.append("描述Build的标签:" +Build.TAGS +"\n");
sb.append("TIME:" +Build.TIME +"\n");
sb.append("builder类型:" +Build.TYPE +"\n");
sb.append("USER:" +Build.USER +"\n");
return sb.toString();
}

    嗯,容易懂,大家可以挑着用,然后就是错误信息,这个错误信息我们是从Throwable里面取出来的:

print.append(throwable.getMessage()).append("\n");
StackTraceElement[] stackTrace = throwable.getStackTrace();
try {
for (int i =0; i < stackTrace.length; i++) {
StackTraceElement stackTraceElement = stackTrace[i];
String trace = stackTraceElement.toString();
print.append(trace +"\n");
crashInfor += trace +"\n";
}
}catch (Exception e) {
e.printStackTrace();
}

只要我们有获取到了Throwable,这些信息全都可以拿到,包括错误的具体位置,那么我们看第三个类的构造器是什么样的,

public static CrashInforMationDetail produce(Throwable throwable, Thread thread, Context context) {
}

里面有Throwable这个参数,也就是说第三类被调用的时候,我们就获取到了Throwable,那么这些错误信息我们就已经拿到了,然后再调用:

public void writeToFile(File file) {
PrintWriter printer =null;
try {
BufferedOutputStream out =new BufferedOutputStream(new FileOutputStream(file,true));
printer =new PrintWriter(out);
printer.println(crashInfor);
printer.flush();
}catch (IOException e) {
e.printStackTrace();
}finally {
if (printer !=null) {
printer.close();
}
}
}

    把错误信息调到第二个类中进行存储成文件,下次程序重新进入的时候就可以上传这些错误文件了,我这里写的三类的怎么使用呢?大家只需要在自己的程序里面初始化第一个类就行了,也就是new一个:new ErrorCaught(this),就可以使用崩溃收集的功能了,不过要注意的就是存储权限要给到。

    好了,大概崩溃的发生和收集想必大家也是比较清晰了,我这里没有写上传的部分,因为上传的网络框架很多,大家可以根据自己的喜欢去挑选即可,重要的是知道这是怎么一个过程就行了,好了,今天的内容就说到这,咱们下次再见...

代码点我:代码:android崩溃日志收集和处理