Android崩溃异常捕获方法
开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。
那么今天主要讲一下如何去捕捉系统出现的Unchecked异常。何为Unchecked异常呢,换句话说就是指非受检异常,它不能用try-catch来显示捕捉。
我们先从Exception讲起。Exception分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effectivejava》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。我们常见的RuntimeExcepiton有IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException等等。对于那些CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。
一、使用UncaughtExceptionHandler来捕获unchecked异常
UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告。
直接上代码吧
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.PrintWriter;
importjava.io.StringWriter;
importjava.io.Writer;
importjava.lang.Thread.UncaughtExceptionHandler;
importjava.lang.reflect.Field;
importjava.text.DateFormat;
importjava.text.SimpleDateFormat;
importjava.util.Date;
importjava.util.HashMap;
importjava.util.Locale;
importjava.util.Map;
importjava.util.Map.Entry;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
importandroid.annotation.SuppressLint;
importandroid.content.Context;
importandroid.content.pm.PackageInfo;
importandroid.content.pm.PackageManager;
importandroid.content.pm.PackageManager.NameNotFoundException;
importandroid.os.Build;
importandroid.os.Environment;
importandroid.os.Looper;
importandroid.util.Log;
importandroid.widget.Toast;
/**
*UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
*
*@authoruser
*
*/
@SuppressLint("SdCardPath")
publicclassCrashHandlerimplementsUncaughtExceptionHandler{
publicstaticfinalStringTAG="TEST";
//CrashHandler实例
privatestaticCrashHandlerINSTANCE=newCrashHandler();
//程序的Context对象
privateContextmContext;
//系统默认的UncaughtException处理类
privateThread.UncaughtExceptionHandlermDefaultHandler;
//用来存储设备信息和异常信息
privateMap<String,String>infos=newHashMap<String,String>();
//用来显示Toast中的信息
privatestaticStringerror="程序错误,额,不对,我应该说,服务器正在维护中,请稍后再试";
privatestaticfinalMap<String,String>regexMap=newHashMap<String,String>();
//用于格式化日期,作为日志文件名的一部分
privateDateFormatformatter=newSimpleDateFormat("yyyy-MM-dd-HH-mm-ss",
Locale.CHINA);
/**保证只有一个CrashHandler实例*/
privateCrashHandler(){
//
}
/**获取CrashHandler实例,单例模式*/
publicstaticCrashHandlergetInstance(){
initMap();
returnINSTANCE;
}
/**
*初始化
*
*@paramcontext
*/
publicvoidinit(Contextcontext){
mContext=context;
//获取系统默认的UncaughtException处理器
mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
Log.d("TEST","Crash:init");
}
/**
*当UncaughtException发生时会转入该函数来处理
*/
@Override
publicvoiduncaughtException(Threadthread,Throwableex){
if(!handleException(ex)&&mDefaultHandler!=null){
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread,ex);
Log.d("TEST","defalut");
}else{
try{
Thread.sleep();
}catch(InterruptedExceptione){
Log.e(TAG,"error:",e);
}
//退出程序
android.os.Process.killProcess(android.os.Process.myPid());
//mDefaultHandler.uncaughtException(thread,ex);
System.exit();
}
}
/**
*自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
*
*@paramex
*@returntrue:如果处理了该异常信息;否则返回false
*/
privatebooleanhandleException(Throwableex){
if(ex==null){
returnfalse;
}
//收集设备参数信息
//collectDeviceInfo(mContext);
//保存日志文件
saveCrashInfoFile(ex);
//使用Toast来显示异常信息
newThread(){
@Override
publicvoidrun(){
Looper.prepare();
Toast.makeText(mContext,error,Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
returntrue;
}
/**
*收集设备参数信息
*
*@paramctx
*/
publicvoidcollectDeviceInfo(Contextctx){
try{
PackageManagerpm=ctx.getPackageManager();
PackageInfopi=pm.getPackageInfo(ctx.getPackageName(),
PackageManager.GET_ACTIVITIES);
if(pi!=null){
StringversionName=pi.versionName==null?"null"
:pi.versionName;
StringversionCode=pi.versionCode+"";
infos.put("versionName",versionName);
infos.put("versionCode",versionCode);
}
}catch(NameNotFoundExceptione){
Log.e(TAG,"anerroroccuredwhencollectpackageinfo",e);
}
Field[]fields=Build.class.getDeclaredFields();
for(Fieldfield:fields){
try{
field.setAccessible(true);
infos.put(field.getName(),field.get(null).toString());
Log.d(TAG,field.getName()+":"+field.get(null));
}catch(Exceptione){
Log.e(TAG,"anerroroccuredwhencollectcrashinfo",e);
}
}
}
/**
*保存错误信息到文件中*
*
*@paramex
*@return返回文件名称,便于将文件传送到服务器
*/
privateStringsaveCrashInfoFile(Throwableex){
StringBuffersb=getTraceInfo(ex);
Writerwriter=newStringWriter();
PrintWriterprintWriter=newPrintWriter(writer);
ex.printStackTrace(printWriter);
Throwablecause=ex.getCause();
while(cause!=null){
cause.printStackTrace(printWriter);
cause=cause.getCause();
}
printWriter.close();
Stringresult=writer.toString();
sb.append(result);
try{
longtimestamp=System.currentTimeMillis();
Stringtime=formatter.format(newDate());
StringfileName="crash-"+time+"-"+timestamp+".log";
if(Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)){
Stringpath=Environment.getExternalStorageDirectory()
+"/crash/";
Filedir=newFile(path);
if(!dir.exists()){
dir.mkdirs();
}
FileOutputStreamfos=newFileOutputStream(path+fileName);
fos.write(sb.toString().getBytes());
fos.close();
}
returnfileName;
}catch(Exceptione){
Log.e(TAG,"anerroroccuredwhilewritingfile...",e);
}
returnnull;
}
/**
*整理异常信息
*@parame
*@return
*/
publicstaticStringBuffergetTraceInfo(Throwablee){
StringBuffersb=newStringBuffer();
Throwableex=e.getCause()==null?e:e.getCause();
StackTraceElement[]stacks=ex.getStackTrace();
for(inti=;i<stacks.length;i++){
if(i==){
setError(ex.toString());
}
sb.append("class:").append(stacks[i].getClassName())
.append(";method:").append(stacks[i].getMethodName())
.append(";line:").append(stacks[i].getLineNumber())
.append(";Exception:").append(ex.toString()+"\n");
}
Log.d(TAG,sb.toString());
returnsb;
}
/**
*设置错误的提示语
*@parame
*/
publicstaticvoidsetError(Stringe){
Patternpattern;
Matchermatcher;
for(Entry<String,String>m:regexMap.entrySet()){
Log.d(TAG,e+"key:"+m.getKey()+";value:"+m.getValue());
pattern=Pattern.compile(m.getKey());
matcher=pattern.matcher(e);
if(matcher.matches()){
error=m.getValue();
break;
}
}
}
/**
*初始化错误的提示语
*/
privatestaticvoidinitMap(){
//Java.lang.NullPointerException
//java.lang.ClassNotFoundException
//java.lang.ArithmeticException
//java.lang.ArrayIndexOutOfBoundsException
//java.lang.IllegalArgumentException
//java.lang.IllegalAccessException
//SecturityException
//NumberFormatException
//OutOfMemoryError
//StackOverflowError
//RuntimeException
regexMap.put(".*NullPointerException.*","嘿,无中生有~Boom!");
regexMap.put(".*ClassNotFoundException.*","你确定你能找得到它?");
regexMap.put(".*ArithmeticException.*","我猜你的数学是体育老师教的,对吧?");
regexMap.put(".*ArrayIndexOutOfBoundsException.*","恩,无下限=无节操,请不要跟我搭话");
regexMap.put(".*IllegalArgumentException.*","你的出生就是一场错误。");
regexMap.put(".*IllegalAccessException.*","很遗憾,你的信用卡账号被冻结了,无权支付");
regexMap.put(".*SecturityException.*","死神马上降临");
regexMap.put(".*NumberFormatException.*","想要改变一下自己形象?去泰国吧,包你满意");
regexMap.put(".*OutOfMemoryError.*","或许你该减减肥了");
regexMap.put(".*StackOverflowError.*","啊,啊,憋不住了!");
regexMap.put(".*RuntimeException.*","你的人生走错了方向,重来吧");
}
}
二、建立一个Application来全局监控
importandroid.app.Application;
publicclassCrashApplicationextendsApplication{
@Override
publicvoidonCreate(){
super.onCreate();
CrashHandlercrashHandler=CrashHandler.getInstance();
crashHandler.init(getApplicationContext());
}
}
最后在配置文件中加入注册信息
<applicationandroid:name=".CrashApplication".../>
和权限
<!--uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/--> <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
提交错误日志到网络服务器这一块还没有添加。如果添加了这一块功能,就能够实时的得要用户使用时的错误日志,能够及时反馈不同机型不同时候发生的错误,能对我们开发者的后期维护带来极大的方便。
有关Android崩溃异常捕获方法小编就给大家介绍这么多,希望对大家有所帮助!