1 public class CrashHandler implements Thread.UncaughtExceptionHandler {
2
3 public static final String TAG = "CrashHandler";
4
5 // 系统默认的UncaughtException处理类
6 private Thread.UncaughtExceptionHandler mDefaultHandler;
7 // CrashHandler实例
8 private static CrashHandler INSTANCE = new CrashHandler();
9 // 程序的Context对象
10 private Context mContext;
11
12 //用来存储设备信息和异常信息
13 private Map<String, String> infos = new HashMap<String, String>();
14
15 //用于格式化日期,作为日志文件名的一部分
16 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
17
18 /**
19 * 保证只有一个CrashHandler实例
20 */
21 private CrashHandler() {
22 }
23
24 /**
25 * 获取CrashHandler实例 ,单例模式
26 */
27 public static CrashHandler getInstance() {
28 return INSTANCE;
29 }
30
31 /**
32 * 初始化
33 * @param context
34 */
35 public void init(Context context) {
36 mContext = context;
37 // 获取系统默认的UncaughtException处理器
38 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
39 // 设置该CrashHandler为程序的默认处理器
40 Thread.setDefaultUncaughtExceptionHandler(this);
41 }
42
43 /**
44 * 当UncaughtException发生时会转入该函数来处理
45 */
46 @Override
47 public void uncaughtException(Thread thread, Throwable ex) {
48 // ToastUtil.show(mContext, ex.getMessage());
49 if (!handleException(ex) && mDefaultHandler != null) {
50 // 如果用户没有处理则让系统默认的异常处理器来处理
51 mDefaultHandler.uncaughtException(thread, ex);
52 } else {
53 try {
54 Thread.sleep(500);
55 } catch (InterruptedException e) {
56 Log.e(TAG, "error : ", e);
57 }
58 // 退出程序
59 android.os.Process.killProcess(android.os.Process.myPid());
60 System.exit(1);
61 // KJActivityStack.create().AppExit(mContext);// 结束整个应用程序
62 }
63 }
64
65 /**
66 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
67 * @param ex
68 * @return true:如果处理了该异常信息;否则返回false.
69 */
70 private boolean handleException(Throwable ex) {
71 if (ex == null) {
72 return false;
73 }
74 // 使用Toast来显示异常信息
75 new Thread() {
76 @Override
77 public void run() {
78 Looper.prepare();
79 Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
80 Looper.loop();
81 }
82 }.start();
83 //保存日志文件
84 String path = saveCrashInfo2File(ex);
85 return true;
86 }
87
88 /**
89 * 保存错误信息到文件中
90 * @param ex
91 * @return 返回文件名称, 便于将文件传送到服务器
92 */
93 private String saveCrashInfo2File(Throwable ex) {
94 infos = PhoneUtils.collectDeviceInfo(mContext);
95 StringBuffer sb = new StringBuffer();
96 for (Map.Entry<String, String> entry : infos.entrySet()) {
97 String key = entry.getKey();
98 String value = entry.getValue();
99 sb.append(key + "=" + value + "
");
100 }
101 Writer writer = new StringWriter();
102 PrintWriter printWriter = new PrintWriter(writer);
103 ex.printStackTrace(printWriter);
104 Throwable cause = ex.getCause();
105 while (cause != null) {
106 cause.printStackTrace(printWriter);
107 cause = cause.getCause();
108 }
109 printWriter.close();
110 String result = writer.toString();
111 sb.append(result);
112 FileOutputStream fos = null;
113 try {
114 long timestamp = System.currentTimeMillis();
115 String time = formatter.format(new Date());
116 String fileName = "crash-" + time + "-" + timestamp + ".log";
117 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
118 String path = "/sdcard/aaaaaaaaaaaaaaaaaaaaacrash/";
119 // String path = mContext.getCacheDir() + "/crash/";
120 File dir = new File(path);
121 if (!dir.exists()) {
122 dir.mkdirs();
123 }
124 fos = new FileOutputStream(path + fileName);
125 fos.write(sb.toString().getBytes());
126 }
127 return fileName;
128 } catch (Exception e) {
129 Log.e(TAG, "an error occured while writing file...", e);
130 } finally {
131 try {
132 if (fos != null)
133 fos.close();
134 } catch (IOException e) {
135 Log.e(TAG, "an error occured while fos.close...", e);
136 }
137 }
138 return null;
139 }