开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。

那么今天主要讲一下如何去捕捉系统出现的Unchecked异常。何为Unchecked异常呢,换句话说就是指非受检异常,它不能用try-catch来显示捕捉。

我们先从Exception讲起。Exception分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effective java》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。我们常见的RuntimeExcepiton有IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException等等。对于那些CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。

一、使用UncaughtExceptionHandler来捕获unchecked异常

UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告。

直接上代码吧

  1 import java.io.File;
2 import java.io.FileOutputStream;
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.io.Writer;
6 import java.lang.Thread.UncaughtExceptionHandler;
7 import java.lang.reflect.Field;
8 import java.text.DateFormat;
9 import java.text.SimpleDateFormat;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.regex.Matcher;
16 import java.util.regex.Pattern;
17
18 import android.annotation.SuppressLint;
19 import android.content.Context;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.os.Build;
24 import android.os.Environment;
25 import android.os.Looper;
26 import android.util.Log;
27 import android.widget.Toast;
28
29 /**
30 * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
31 *
32 * @author user
33 *
34 */
35 @SuppressLint("SdCardPath")
36 public class CrashHandler implements UncaughtExceptionHandler {
37
38 public static final String TAG = "TEST";
39
40 // CrashHandler 实例
41 private static CrashHandler INSTANCE = new CrashHandler();
42
43 // 程序的 Context 对象
44 private Context mContext;
45
46 // 系统默认的 UncaughtException 处理类
47 private Thread.UncaughtExceptionHandler mDefaultHandler;
48
49 // 用来存储设备信息和异常信息
50 private Map<String, String> infos = new HashMap<String, String>();
51
52 // 用来显示Toast中的信息
53 private static String error = "程序错误,额,不对,我应该说,服务器正在维护中,请稍后再试";
54
55 private static final Map<String, String> regexMap = new HashMap<String, String>();
56
57 // 用于格式化日期,作为日志文件名的一部分
58 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss",
59 Locale.CHINA);
60
61 /** 保证只有一个 CrashHandler 实例 */
62 private CrashHandler() {
63 //
64 }
65
66 /** 获取 CrashHandler 实例 ,单例模式 */
67 public static CrashHandler getInstance() {
68 initMap();
69 return INSTANCE;
70 }
71
72 /**
73 * 初始化
74 *
75 * @param context
76 */
77 public void init(Context context) {
78 mContext = context;
79
80 // 获取系统默认的 UncaughtException 处理器
81 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
82
83 // 设置该 CrashHandler 为程序的默认处理器
84 Thread.setDefaultUncaughtExceptionHandler(this);
85 Log.d("TEST", "Crash:init");
86 }
87
88 /**
89 * 当 UncaughtException 发生时会转入该函数来处理
90 */
91 @Override
92 public void uncaughtException(Thread thread, Throwable ex) {
93 if (!handleException(ex) && mDefaultHandler != null) {
94 // 如果用户没有处理则让系统默认的异常处理器来处理
95 mDefaultHandler.uncaughtException(thread, ex);
96 Log.d("TEST", "defalut");
97 } else {
98 try {
99 Thread.sleep(3000);
100 } catch (InterruptedException e) {
101 Log.e(TAG, "error : ", e);
102 }
103 // 退出程序
104 android.os.Process.killProcess(android.os.Process.myPid());
105 // mDefaultHandler.uncaughtException(thread, ex);
106 System.exit(1);
107 }
108 }
109
110 /**
111 * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
112 *
113 * @param ex
114 * @return true:如果处理了该异常信息;否则返回 false
115 */
116 private boolean handleException(Throwable ex) {
117 if (ex == null) {
118 return false;
119 }
120
121 // 收集设备参数信息
122 // collectDeviceInfo(mContext);
123 // 保存日志文件
124 saveCrashInfo2File(ex);
125 // 使用 Toast 来显示异常信息
126 new Thread() {
127 @Override
128 public void run() {
129 Looper.prepare();
130 Toast.makeText(mContext, error, Toast.LENGTH_LONG).show();
131 Looper.loop();
132 }
133 }.start();
134 return true;
135 }
136
137 /**
138 * 收集设备参数信息
139 *
140 * @param ctx
141 */
142 public void collectDeviceInfo(Context ctx) {
143 try {
144 PackageManager pm = ctx.getPackageManager();
145 PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
146 PackageManager.GET_ACTIVITIES);
147
148 if (pi != null) {
149 String versionName = pi.versionName == null ? "null"
150 : pi.versionName;
151 String versionCode = pi.versionCode + "";
152 infos.put("versionName", versionName);
153 infos.put("versionCode", versionCode);
154 }
155 } catch (NameNotFoundException e) {
156 Log.e(TAG, "an error occured when collect package info", e);
157 }
158
159 Field[] fields = Build.class.getDeclaredFields();
160 for (Field field : fields) {
161 try {
162 field.setAccessible(true);
163 infos.put(field.getName(), field.get(null).toString());
164 Log.d(TAG, field.getName() + " : " + field.get(null));
165 } catch (Exception e) {
166 Log.e(TAG, "an error occured when collect crash info", e);
167 }
168 }
169 }
170
171 /**
172 * 保存错误信息到文件中 *
173 *
174 * @param ex
175 * @return 返回文件名称,便于将文件传送到服务器
176 */
177 private String saveCrashInfo2File(Throwable ex) {
178 StringBuffer sb = getTraceInfo(ex);
179 Writer writer = new StringWriter();
180 PrintWriter printWriter = new PrintWriter(writer);
181 ex.printStackTrace(printWriter);
182 Throwable cause = ex.getCause();
183 while (cause != null) {
184 cause.printStackTrace(printWriter);
185 cause = cause.getCause();
186 }
187 printWriter.close();
188
189 String result = writer.toString();
190 sb.append(result);
191 try {
192 long timestamp = System.currentTimeMillis();
193 String time = formatter.format(new Date());
194 String fileName = "crash-" + time + "-" + timestamp + ".log";
195
196 if (Environment.getExternalStorageState().equals(
197 Environment.MEDIA_MOUNTED)) {
198 String path = Environment.getExternalStorageDirectory()
199 + "/crash/";
200 File dir = new File(path);
201 if (!dir.exists()) {
202 dir.mkdirs();
203 }
204 FileOutputStream fos = new FileOutputStream(path + fileName);
205 fos.write(sb.toString().getBytes());
206 fos.close();
207 }
208
209 return fileName;
210 } catch (Exception e) {
211 Log.e(TAG, "an error occured while writing file...", e);
212 }
213
214 return null;
215 }
216
217 /**
218 * 整理异常信息
219 * @param e
220 * @return
221 */
222 public static StringBuffer getTraceInfo(Throwable e) {
223 StringBuffer sb = new StringBuffer();
224
225 Throwable ex = e.getCause() == null ? e : e.getCause();
226 StackTraceElement[] stacks = ex.getStackTrace();
227 for (int i = 0; i < stacks.length; i++) {
228 if (i == 0) {
229 setError(ex.toString());
230 }
231 sb.append("class: ").append(stacks[i].getClassName())
232 .append("; method: ").append(stacks[i].getMethodName())
233 .append("; line: ").append(stacks[i].getLineNumber())
234 .append("; Exception: ").append(ex.toString() + "\n");
235 }
236 Log.d(TAG, sb.toString());
237 return sb;
238 }
239
240 /**
241 * 设置错误的提示语
242 * @param e
243 */
244 public static void setError(String e) {
245 Pattern pattern;
246 Matcher matcher;
247 for (Entry<String, String> m : regexMap.entrySet()) {
248 Log.d(TAG, e+"key:" + m.getKey() + "; value:" + m.getValue());
249 pattern = Pattern.compile(m.getKey());
250 matcher = pattern.matcher(e);
251 if(matcher.matches()){
252 error = m.getValue();
253 break;
254 }
255 }
256 }
257
258 /**
259 * 初始化错误的提示语
260 */
261 private static void initMap() {
262 // Java.lang.NullPointerException
263 // java.lang.ClassNotFoundException
264 // java.lang.ArithmeticException
265 // java.lang.ArrayIndexOutOfBoundsException
266 // java.lang.IllegalArgumentException
267 // java.lang.IllegalAccessException
268 // SecturityException
269 // NumberFormatException
270 // OutOfMemoryError 
271 // StackOverflowError 
272 // RuntimeException 
273 regexMap.put(".*NullPointerException.*", "嘿,无中生有~Boom!");
274 regexMap.put(".*ClassNotFoundException.*", "你确定你能找得到它?");
275 regexMap.put(".*ArithmeticException.*", "我猜你的数学是体育老师教的,对吧?");
276 regexMap.put(".*ArrayIndexOutOfBoundsException.*", "恩,无下限=无节操,请不要跟我搭话");
277 regexMap.put(".*IllegalArgumentException.*", "你的出生就是一场错误。");
278 regexMap.put(".*IllegalAccessException.*", "很遗憾,你的信用卡账号被冻结了,无权支付");
279 regexMap.put(".*SecturityException.*", "死神马上降临");
280 regexMap.put(".*NumberFormatException.*", "想要改变一下自己形象?去泰国吧,包你满意");
281 regexMap.put(".*OutOfMemoryError.*", "或许你该减减肥了");
282 regexMap.put(".*StackOverflowError.*", "啊,啊,憋不住了!");
283 regexMap.put(".*RuntimeException.*", "你的人生走错了方向,重来吧");
284
285 }
286
287 }

二、建立一个Application来全局监控

import android.app.Application;

public class CrashApplication extends Application {

    @Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(getApplicationContext());
} }

最后在配置文件中加入注册信息

<application android:name=".CrashApplication" ... />

和权限

    <!--uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

提交错误日志到网络服务器这一块还没有添加。如果添加了这一块功能,就能够实时的得要用户使用时的错误日志,能够及时反馈不同机型不同时候发生的错误,能对我们开发者的后期维护带来极大的方便。

Android捕获崩溃异常的更多相关文章

  1. Android application捕获崩溃异常

    Java代码 .收集所有 avtivity 用于彻底退出应用 .捕获崩溃异常,保存错误日志,并重启应用 , intent, , restartIntent); // 关闭当前应用 finishAllA ...

  2. Android捕获全局异常

    Android捕获全局异常 程序避免不了出现bug,导致程序崩溃,为了尽量不影响用户体验,可以全局捕获异常 效果图 异常捕获处理前 异常捕获处理后(将程序重新启动) 捕获异常的工具类 package ...

  3. Android程序崩溃异常收集框架

    最近在写Android程序崩溃异常处理,完成之后,稍加封装与大家分享. 我的思路是这样的,在程序崩溃之后,将异常信息保存到一个日志文件中,然后对该文件进行处理,比如发送到邮箱,或发送到服务器. 所以, ...

  4. android捕获程序异常退出

    今天看到迅雷动漫里面一个CrashHandler 的类,我猜是崩溃处理类.进去一看.果然.顺便学习一下. Android系统的"程序异常退出",给应用的用户体验造成不良影响.为了捕 ...

  5. Android开发之处理崩溃异常

    众所周知,android的设备千差万别,难免会发生崩溃异常等现象,这个时候就需要捕获哪些崩溃异常了,也就是捕获崩溃异常的相关信息,并记录下来,这样一来方便开发人员和测试人员的分析与调试. 1.首先我们 ...

  6. 【转】Android 中处理崩溃异常并重启程序出现页面重叠的问题

    原文地址:http://blog.csdn.net/jiang547860818/article/details/53641113 android开发中经常会遇到程序异常,而已常常会遇到一出现异常AP ...

  7. android 捕获未try的异常、抓取崩溃日志

    1.Thread.UncaughtExceptionHandler java里有很多异常如:空指针异常,越界异常,数值转换异常,除0异常,数据库异常等等.如果自己没有try / catch 那么线程就 ...

  8. Android中处理崩溃异常

    转自:http://my.eoe.cn/817027/archive/17997.html 大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不 ...

  9. Android中处理崩溃异常和记录日志

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

随机推荐

  1. C#语言的新特性及相关信息

     .ENT版本 NET 2.0 :CLR, WinForms ,Wed Services,ASP.NET NET 3.0 :WCF,WF,WPF,CardSpace NET 3.5 :LINQ ,AJ ...

  2. Thinkphp 零散知识点(caa/js路径,引入第三方类,ajax返回,session/cookie)

    一.关于JS和CSS路径问题 1.找路径是从入口文件index.php来找的,而不是从文件本身所在位置来找, 因为我们访问时是访问的入口文件 2.在存放JS和CSS的时候可以放到public文件夹下 ...

  3. 网易DBA私享会分享会笔记1

    1.mysql生态+DBA职业发展世界范围流行的开源关系型的数据库关系型数据库:mysql,oracle,sql server,access,postgresql,sqlite,sybase,info ...

  4. Python数据类型list(列表)和tuple(元组)

    list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> ...

  5. 使用Ajax.ActionLink时,点击对应的按钮会重新加载一个页面,而不是在当前页面的指定模块加载

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. 如何让div出现滚动条

    由于DIV本身属性并没有滚动条设置,但是有些地方的设计却需要出现滚动条,如何实现呢?本人采用CSS样式来控制显示!而且也很简单,代码如下: <div style="OVERFLOW-Y ...

  7. SQL Server DAC 管理员专用连接

    DAC 是Dedicated Administrator Connect 的缩写,是专用管理员连接通道,当SQL Server 因为资源不足而无法连接入系统的时候,管理员可通过该通道连接到数据,进行问 ...

  8. ACCESS中类型操作(限制、转换)

    ACCESS如何保留两位小数 1.可以通过修改表结构中字段的“小数位数”即可. 2.可以通过“更新查询”,将所有该字段的值更新为round(字段名,2) ACCESS如何转换类型 每个函数都可以强制将 ...

  9. 《30天自制操作系统》读书笔记(3) 引入C语言

    这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...

  10. Solr4.8.0源码分析(13)之LuceneCore的索引修复

    Solr4.8.0源码分析(13)之LuceneCore的索引修复 题记:今天在公司研究elasticsearch,突然看到一篇博客说elasticsearch具有索引修复功能,顿感好奇,于是点进去看 ...