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崩溃异常捕获方法小编就给大家介绍这么多,希望对大家有所帮助!