android 捕捉异常并上传至服务器的简单实现
在项目中,我们的应用经常会遇到崩溃的情况,如果你的项目已经发送到了应用市场上,那么应用发生的崩溃开发人员是开不到的,所以我们要想办法将异常信息传到服务器上,便于开发人员查看并作出修改。Google考虑到这一点,也提供了Thread.UncaughtExceptionHandler接口来实现这一问题。
创建Crash异常捕获很简单,主要的步骤有:
1.创建BaseApplication继承Application并实现Thread.UncaughtExceptionHandler
2.通过Thread.setDefaultUncaughtExceptionHandler(this)设置默认的异常捕获
3.最后在manifests中注册创建的BaseApplication
一、异常捕捉的简单实用
publicclassBaseApplicationextendsApplicationimplementsThread.UncaughtExceptionHandler{ @Override publicvoidonCreate(){ super.onCreate(); //设置异常捕获 CrashHandlercatchHandler=CrashHandler.getInstance(); catchHandler.init(this); } }
二、CrashHandler(主要是实现uncaughtException方法)
publicclassCrashHandlerimplementsUncaughtExceptionHandler{ publicstaticfinalStringTAG="CrashHandler"; //系统默认的UncaughtException处理类 privateThread.UncaughtExceptionHandlermDefaultHandler; //CrashHandler实例 privatestaticCrashHandlerinstance; //程序的Context对象 privateContextmContext; //用来存储设备信息和异常信息 privateMapinfos=newHashMap (); //用于格式化日期,作为日志文件名的一部分 privateDateFormatformatter=newSimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); MyActivityLifecycleCallbacksmMyActivityLifecycleCallbacks=newMyActivityLifecycleCallbacks(); /**保证只有一个CrashHandler实例*/ privateCrashHandler(){ } /**获取CrashHandler实例,单例模式*/ publicstaticCrashHandlergetInstance(){ if(instance==null) instance=newCrashHandler(); returninstance; } /** *初始化 */ publicvoidinit(SspApplicationcontext){ mContext=context; context.registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks); //获取系统默认的UncaughtException处理器 mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** *当UncaughtException发生时会转入该函数来处理 */ @Override publicvoiduncaughtException(Threadthread,Throwableex){ if(!handleException(ex)&&mDefaultHandler!=null){ //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread,ex); }else{ //try{ //Thread.sleep(3000); //}catch(InterruptedExceptione){ //Log.e(TAG,"error:",e); //} //注意Thread.sleep(3000)和SystemClock.sleep(3000)的区别 SystemClock.sleep(3000); //退出程序 Log.i("=====killProcess======","=====killProcess======"); mMyActivityLifecycleCallbacks.removeAllActivities(); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(0); } } /** *自定义错误处理,收集错误信息发送错误报告等操作均在此完成. * *@paramex *@returntrue:如果处理了该异常信息;否则返回false. */ privatebooleanhandleException(Throwableex){ Log.i("=====handleException======","=====handleException======"); if(ex==null){ returnfalse; } //收集设备参数信息 collectDeviceInfo(mContext); //使用Toast来显示异常信息 newThread(){ @Override publicvoidrun(){ Looper.prepare(); Toast.makeText(mContext,"哎呀,出问题了,我要暂时离开了",Toast.LENGTH_SHORT).show(); Looper.loop(); } }.start(); //保存日志文件 saveCatchInfo2File(ex); 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返回文件名称,便于将文件传送到服务器 */ privateStringsaveCatchInfo2File(Throwableex){ StringBuffersb=newStringBuffer(); for(Map.Entry entry:infos.entrySet()){ Stringkey=entry.getKey(); Stringvalue=entry.getValue(); sb.append(key+"="+value+"\n"); } 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()); SharedPreferencesuserInfo=mContext.getSharedPreferences( Constants.USER_SETTING_INFOS,0); StringloginName=userInfo.getString(Constants.USERNAME,""); StringfileName="crash-"+time+"-"+loginName+".log"; if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ Stringpath="/mnt/sdcard/crash/"; Filedir=newFile(path); if(!dir.exists()){ dir.mkdirs(); } FileOutputStreamfos=newFileOutputStream(path+fileName); fos.write(sb.toString().getBytes()); //发送给开发人员 sendCrashLog2PM(path+fileName); fos.close(); } returnfileName; }catch(Exceptione){ Log.e(TAG,"anerroroccuredwhilewritingfile...",e); } returnnull; } /** *将捕获的导致崩溃的错误信息发送给开发人员 * *目前只将log日志保存在sdcard和输出到LogCat中,并未发送给后台。 */ privatevoidsendCrashLog2PM(finalStringfileName){ if(!newFile(fileName).exists()){ Toast.makeText(mContext,"日志文件不存在!",Toast.LENGTH_SHORT).show(); return; }else{ newThread(newRunnable(){ @Override publicvoidrun(){ Looper.prepare(); ArrayList picList=newArrayList (); picList.add(fileName); SendEventPicsFile=newSendEventPic(mContext); UUIDGeneratorgenerator=newUUIDGenerator(); StringlinkId=generator.generate().toString(); SharedPreferencesuserInfo=mContext.getSharedPreferences( Constants.USER_SETTING_INFOS,0); StringloginName=userInfo.getString(Constants.USERNAME,""); StringuserId=userInfo.getString(Constants.USER_USERID_INFOS,""); booleanisproblempic=sFile.isSendSuccess(picList,linkId, "crash_tng","crash_tng",loginName,userId); Looper.loop(); } }).start(); } FileInputStreamfis=null; BufferedReaderreader=null; Strings=null; try{ fis=newFileInputStream(fileName); reader=newBufferedReader(newInputStreamReader(fis,"GBK")); while(true){ s=reader.readLine(); if(s==null) break; //由于目前尚未确定以何种方式发送,所以先打出log日志。 Log.i("info",s.toString()); } }catch(FileNotFoundExceptione){ e.printStackTrace(); }catch(IOExceptione){ e.printStackTrace(); }finally{//关闭流 try{ reader.close(); fis.close(); }catch(IOExceptione){ e.printStackTrace(); } } } }
三、开发过程中遇到的坑
如果在activity创建的时候崩溃的话,系统有时候(目前不确定什么情况下会重启)会重启当前的activity,造成第二次的崩溃,如此循环……
所以,在应用崩溃时要完全退出应用。
publicclassMyActivityLifecycleCallbacksimplementsActivityLifecycleCallbacks{ privateListactivities=newLinkedList<>(); publicstaticintsAnimationId=0; @Override publicvoidonActivityCreated(Activityactivity,BundlesavedInstanceState){ addActivity(activity); } @Override publicvoidonActivityStarted(Activityactivity){ } @Override publicvoidonActivityResumed(Activityactivity){ } @Override publicvoidonActivityPaused(Activityactivity){ } @Override publicvoidonActivityStopped(Activityactivity){ } @Override publicvoidonActivitySaveInstanceState(Activityactivity,BundleoutState){ } @Override publicvoidonActivityDestroyed(Activityactivity){ removeActivity(activity); } /** *添加Activity */ publicvoidaddActivity(Activityactivity){ if(activities==null){ activities=newLinkedList<>(); } if(!activities.contains(activity)){ activities.add(activity);//把当前Activity添加到集合中 } } /** *移除Activity */ publicvoidremoveActivity(Activityactivity){ if(activities.contains(activity)){ activities.remove(activity); } if(activities.size()==0){ activities=null; } } /** *销毁所有activity */ publicvoidremoveAllActivities(){ for(Activityactivity:activities){ if(null!=activity){ activity.finish(); activity.overridePendingTransition(0,sAnimationId); } } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。