概要
在Android开发中,目前没有工业级的bug收集工具,不比java后台服务有各式各样的收集工具,Android用的更多一些
第三方的如bugly或者友盟等。那如何自己去收集呢?其实安卓原生提供了异常收集类UncaughtExceptionHandler。
UncaughtExceptionHandler正如其名称所示,用于收集未捕获的异常(简单的理解可以认为是崩溃异常)。
具体使用分为两步:
- 实现自定义的异常处理类:
1
2
3
4
5
6public class CrashHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread thread, Throwable ex) {
//异常具体的处理,记录亦或上传到服务器
}
}
实现Thread.UncaughtExceptionHandler接口当有未捕获的异常的时候会回调uncaughtException(Thread thread, Throwable ex)方法
- 设置自定的异常处理类为系统默认的
1
Thread.setDefaultUncaughtExceptionHandler(crashHandler);
以上就是大致的使用步骤
具体代码逻辑
在application中初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class AppApplication extends Application {
private static Context mContext;
public void onCreate() {
super.onCreate();
this.mContext = this;
CrashHandler.getInstance().init(this);
}
public static Context getContext(){
return mContext;
}
}CrashHandler 实现类如下
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
168public class CrashHandler implements Thread.UncaughtExceptionHandler {
/**
* 系统默认UncaughtExceptionHandler
*/
private Thread.UncaughtExceptionHandler mDefaultHandler;
/**
* context
*/
private Context mContext;
/**
* 存储异常和参数信息
*/
private Map 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();
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* uncaughtException 回调函数
*/
public void uncaughtException(Thread thread, Throwable ex) {
if(!handleException(ex) && mDefaultHandler != null){
mDefaultHandler.uncaughtException(thread,ex);
}else{
try {
Thread.sleep(3000);
} 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);
new Thread() {
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) {
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);
}
}
}
/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry 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());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}
⚠️此处并未使用常用的退出应用代码1
2
3
4android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
//区别在于:system.exit(0):正常退出,程序正常执行结束退出,system.exit(1):是非正常退出,就是说无论程序正在执行与否,都退出,
//System.exit(status)不管status为何值都会退出程序。和return 相比有以下不同点:return是回到上一层,而System.exit(status)是回到最上层 */
具体原因为:在4.4即以上系统使用该方法杀进程的时候app会重启,类似内存不足app挂了,系统会发广播拉起app的那种情况,那么就有可能会导致一个异常多次上报的情况,所以这里实现的activity栈来管理退出.
- activity管理类
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
95public class AppManager {
private static Stack activityStack;
private static AppManager instance;
private AppManager() {
}
/**
* 单一实例
*/
public static AppManager getAppManager() {
if (instance == null) {
instance = new AppManager();
}
return instance;
}
/**
* 添加Activity到堆栈
*/
public void addActivity(Activity activity) {
if (activityStack == null) {
activityStack = new Stack();
}
activityStack.add(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public Activity currentActivity() {
Activity activity = activityStack.lastElement();
return activity;
}
/**
* 结束当前Activity(堆栈中最后一个压入的)
*/
public void finishActivity() {
Activity activity = activityStack.lastElement();
finishActivity(activity);
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
activity.finish();
activity = null;
}
}
/**
* 结束指定类名的Activity
*/
public void finishActivity(Class cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
break;
}
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
for (int i = 0; i < activityStack.size(); i++) {
if (null != activityStack.get(i)) {
activityStack.get(i).finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager activityMgr = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
activityMgr.killBackgroundProcesses(context.getPackageName());
System.exit(0);
} catch (Exception e) {
}
}
}