Sunxin'Blog

Android-构建全局的异常捕捉类

Android-构建全局的异常捕捉类

整体思路

​ 构建Android应用全局的异常信息捕捉类,当App Crash的时候,能够获取崩溃信息,包括ExceptionMessage,手机信息,版本信息等。然后写入文件存到手机sd卡中。当应用下次开启的时候读取文件上传服务器,为后期的热修复做准备。

  • 首先新建一个ExceptionCrashHandler类实现Thread.UncaughtExceptionHandler接口,并重写uncaughtException(Thread t, Throwable e)方法,这个方法当有异常发生的时候会被回调。
  • ExceptionCrashHandler整个应用有一份即可,所以可以使用单例模式要构建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static ExceptionCrashHandler mInstance;
private Context mContext;
private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;

public static ExceptionCrashHandler getInstance() {
if (mInstance == null) {
//同步锁,处理并发问题
synchronized (ExceptionCrashHandler.class) {
if (mInstance == null) {
mInstance = new ExceptionCrashHandler();
}
}
}
return mInstance;
}

public void init(Context context) {
mContext = context;
//设置全局的异常类为本类
Thread.currentThread().setUncaughtExceptionHandler(this);
//获取系统默认的异常处理
mDefaultExceptionHandler = Thread.currentThread().getDefaultUncaughtExceptionHandler();
}
  • 在全局的Application中初始化。
1
ExceptionCrashHandler.getInstance().init(this);
  • 在uncaughtException方法中去做一些信息的收集工作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public void uncaughtException(Thread t, Throwable e) {
//全局异常
Logger.e("报异常了!");
//拿到异常信息,写到本地文件,包括异常信息,机型信息,当前版本等
//1,异常信息
//2,应用信息,包名 版本号
//3,手机机型信息
//4,保存当前文件,当应用再次启动的时候再去上传。 上传文件不能在这里处理

String crashFileName = saveInfo2SD(e);

Logger.d("CrashFileName--->" + crashFileName);

//缓存崩溃日志
cacheCrashLog(crashFileName);

//系统默认的处理方式
mDefaultExceptionHandler.uncaughtException(t, e);
}
  • 保存信息到SD卡中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 保存异常信息,软件信息,设备信息到sd卡中
*
* @param e
* @return
*/
private String saveInfo2SD(Throwable e) {
String fileName = null;
StringBuffer stringBuffer = new StringBuffer();
Map<String, String> simpleInfo = getSimpleInfo(mContext);
//循环获取map中的键值对,拼接到StringBuffer中
for (Map.Entry<String, String> entry : simpleInfo.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
stringBuffer.append(key).append("=").append(value).append("\n");
}

//拼接系统异常信息
stringBuffer.append(getExceptionInfo(e));

//将数据写入到Sd卡中
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//sd卡可用
File dir = new File(mContext.getFilesDir() + File.separator + "crash" + File.separator);

//删除之前的异常信息
if (dir.exists()) {
deleteDir(dir);
}

//在创建新的文件夹
if (!dir.exists()) {
dir.mkdirs();
}

try {
fileName = dir.toString() + File.separator + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt";
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(stringBuffer.toString().getBytes());
fos.flush();
fos.close();

} catch (Exception ex) {
ex.printStackTrace();
}
}
return fileName;
}
  • 获取手机信息可以通过反射,获取Build类中的字段信息。这里面可以拿到手机型号,版本信息。可能某些bug是在特定机型上才出现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 利用反射Build类获取手机信息
*
* @return
*/
private String getMobileInfo() {
StringBuffer stringBuffer = new StringBuffer();
//利用反射获取字段
Field[] fields = Build.class.getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();//获取字段名
String value = field.get(null).toString();//获取值

stringBuffer.append(name + "=" + value);
stringBuffer.append("\n");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return stringBuffer.toString();
}
  • 还要对外提供一个获取异常信息文件的方法,用于再次启动应用的时候获取该文件并上传
1
2
3
4
5
6
7
8
9
10
/**
* 获取崩溃的文件
*
* @return
*/
public File getCrashFile() {
SharedPreferences sharedPreferences = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE);
String carshFileName = sharedPreferences.getString("CRASH_FILE_NAME", "");
return new File(carshFileName);
}
  • 在MainActivity启动的时候,拿到该文件直接上传服务器,我们这里没有服务器,就直接把信息打印出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (crashFile.exists()){
//如果文件存在,上传服务器 。。。
Logger.d("上传到服务器");
//读取出来
try {
FileReader fileReader = new FileReader(crashFile);
int len = 0;

char[] buf = new char[1024];
while ((len = fileReader.read(buf)) != -1){
//继续读
String crashInfo = new String(buf, 0, len);
Logger.d(crashInfo);
}

} catch (Exception e) {
e.printStackTrace();
}
}
  • 写完之后可以在应用的任何地方写一个异常试一试,第一次点击挂掉,然后再点一次就可以拿到崩溃信息了。

源码:

https://github.com/Sun0630/EssayJoke/blob/master/baselibrary/src/main/java/com/sx/baselibrary/ExceptionCrashHandler.java

总结: 这样当应用崩溃的时候开发人员就可以及时的获取崩溃信息,针对性的修复bug,做热修复等等。当然bug监控也有很好的第三方平台,友盟和腾讯的Bugly都不错。可以自己选择。

-------------本文结束感谢您的阅读-------------