android 异常捕获-UncaughtExceptionHandler

在android开发中,异常信息的捕获有多种方式,比如第三方的友盟、蒲公英等,这里主要使用 android 原生的 Thread.UncaughtExceptionHandler 来捕获出现的异常信息,并给出友好的提示,避免出现停止运行,提高用户体验,下面是具体的实现过程。

转自: Android 全局异常捕获

1.定义自己的异常处理类

新建类 CrashHandler 实现 Thread.UncaughtExceptionHandler接口,如下代码:

1
2
3
4
5
6
7
public class CrashHandler implements Thread.UncaughtExceptionHandler {

@Override
public void uncaughtException(Thread thread, Throwable ex) {
//回调函数,处理异常出现后的情况
}
}

2.设置该异常类为系统默认的

将上面定义的异常处理类设置为系统默认的异常处理类,当出现异常时,有该类处理。

1
Thread.setDefaultUncaughtExceptionHandler(crashHandler);

3.具体的实现

为了方便查看和修复异常,这里将出现异常的手机信息和异常信息保存在文件,以便传给服务器,具体实现如下:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* 异常管理类
* <p/>
* Created by imtianx on 2016-7-10.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {

/**
* 系统默认UncaughtExceptionHandler
*/
private Thread.UncaughtExceptionHandler mDefaultHandler;

/**
* context
*/
private Context mContext;

/**
* 存储异常和参数信息
*/
private Map<String, String> paramsMap = new HashMap<>();

/**
* 格式化时间
*/
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

private String TAG = this.getClass().getSimpleName();

private static CrashHandler mInstance;

private CrashHandler() {

}

/**
* 获取CrashHandler实例
*/
public static synchronized CrashHandler getInstance() {
if (null == mInstance) {
mInstance = new CrashHandler();
}
return mInstance;
}

public void init(Context context) {
mContext = context;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为系统默认的
Thread.setDefaultUncaughtExceptionHandler(this);
}

/**
* uncaughtException 回调函数
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
//如果自己没处理交给系统处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
//自己处理
try {//延迟3秒杀进程
Thread.sleep(2000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
}
//退出程序
AppManager.getAppManager().AppExit(mContext);
}

}

/**
* 收集错误信息.发送到服务器
*
* @return 处理了该异常返回true, 否则false
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
//收集设备参数信息
collectDeviceInfo(mContext);
//添加自定义信息
addCustomInfo();
//使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
//在此处处理出现异常的情况
Toast.makeText(mContext, "程序开小差了呢..", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
//保存日志文件
saveCrashInfo2File(ex);
return true;
}


/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
//获取versionName,versionCode
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
paramsMap.put("versionName", versionName);
paramsMap.put("versionCode", versionCode);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
//获取所有系统信息
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
paramsMap.put(field.getName(), field.get(null).toString());
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
}

/**
* 添加自定义参数
*/
private void addCustomInfo() {
Log.i(TAG, "addCustomInfo: 程序出错了...");
}

/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {

StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}

Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = format.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".log";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/crash/";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(path + fileName);
fos.write(sb.toString().getBytes());
Log.i(TAG, "saveCrashInfo2File: "+sb.toString());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}

注:在上述代码中使用了 AppManager类,是常用的activity 管理类 点此查看

4.实际使用

在application中初始化,并在配置文件中添加读写权限。

1
CrashHandler.getInstance().init(this);

到此,全局异常捕获已全部完成。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器