Android实现多次闪退清除数据
很多时候由于后台返回的数据异常,可能会导致App闪退。而如果这些异常数据被App本地缓存下来,那么即使杀掉进程重新进入还是会发生闪退。唯一的解决方法就是清除App数据,但是用户可能没有这个意识或者嫌麻烦就直接不再使用了,这是我们无法接受的。在使用淘宝、追书神器等App时我发现有时候它们也会连续闪退,但是往往闪退三次后就恢复正常了,所以一般成熟的App都会做连续闪退三次后清除缓存数据的工作。而目前笔者搜不到有哪篇blog来讲这方面的事情,所以就姑且由我来讲讲此事,为希望提高App用户体验的朋友提供些许参考。
ACRA
为了能够在闪退的时候做一些事情,我们可以使用ACRA,这是Github上的一个开源项目,允许使用者设置一些Sender在App闪退的时候做一些事情。具体使用可以直接参考Github。如果不希望使用ACRA,那么也可以自己实现一个UncachedExceptionHandler并替换系统默认的Handler,并在这个Handler里面对数据进行处理。
实现清除数据
ACRA提供了自己的一些Sender,如使用系统邮件客户端向指定邮箱发送邮件的EmailIntentSender。而我们希望记录闪退次数和清除数据则需要implementsReportSender接口。
publicclassCrashHandlerimplementsReportSender{ @Override publicvoidsend(Contextcontext,CrashReportDataerrorContent)throwsReportSenderException{ Timber.i("闪退,检查是否需要清空数据"); newCrashModel().checkAndClearData(); } }
这里我们写了一个CrashModel用来记录闪退次数和时间决定是否需要清空数据,具体代码如下。由于在ReportSender的时候无法打开其它线程,所以我们无法使用SharedPerferences来清理数据(打开SP的时候其实打开了一个新线程)。为此需要找到数据缓存的位置并将文件删除。同样道理,记录闪退时间也只能通过文件记录。当然,你可以选择一些文件不进行删除,如用户信息等不太容易出问题的数据。
publicclassCrashModel{ privatestaticfinalStringKEY_CRASH_TIMES="crash_times"; privatestaticfinalStringCRASH_TIME_FILE_NAME="crash_time"; //不能通过App.getPackageName来获取包名,否则会有问题,只能默认为cn.campusapp.campus。所以对于debug或者运营版本,清数据会把release的清掉 privatestaticfinalStringFILE_DIR=String.format("/data/data/%s/",BuildConfig.APPLICATION_ID); privatestaticfinalStringACCOUNT_FILE_NAME=String.format("%s%s",FILE_DIR,"shared_prefs/account_pref.xml"); privatestaticArrayList<String>FILES_DONTNEED_DELETE=newArrayList<>();//该目录中的文件不会被删除 static{ FILES_DONTNEED_DELETE.add(ACCOUNT_FILE_NAME);//目前账号信息文件不会被删除,但是会手动改变数据,只保留userIdaccessToken和school } protectedArrayList<Long>mCrashTimes; Gsongson=newGson(); privateFilemFileDir; publicCrashModel(){ mFileDir=newFile(FILE_DIR); mCrashTimes=readCrashTimes(); if(mCrashTimes==null){ mCrashTimes=newArrayList<>(); storeCrashTimes(mCrashTimes); } } publicvoidcheckAndClearData(){ longtimeNow=System.currentTimeMillis(); if(checkClearData(timeNow,newArrayList<>(mCrashTimes))){ Timber.i("已经在5分钟之内有三次闪退,需要清理数据"); try{ clearData(); }catch(Exceptione){ Timber.e(e,"清空所有数据失败"); } }else{ mCrashTimes.add(timeNow); storeCrashTimes(mCrashTimes); Timber.i("此次不需要清空数据,%s",gson.toJson(mCrashTimes)); } } privatevoidstoreCrashTimes(ArrayList<Long>crashTimes){ try{ Stringstr=gson.toJson(crashTimes); Files.writeToFile(mFileDir,CRASH_TIME_FILE_NAME,str); }catch(Exceptione){ Timber.e(e,"保存闪退时间失败"); } } privateArrayList<Long>readCrashTimes(){ try{ StringtimeStr=Files.readFileContent(mFileDir,CRASH_TIME_FILE_NAME); returngson.fromJson(timeStr,newTypeToken<ArrayList<Long>>(){ }.getType()); }catch(Exceptione){ Timber.e(e,"读取闪退时间失败"); } returnnull; } /** *检查是否需要清空数据,目前的清空策略是在5分钟之内有三次闪退的就清空数据,也就是从后往前遍历,只要前两次闪退发生在5分钟之内,就清空数据 * *@return */ privatebooleancheckClearData(longtime,ArrayList<Long>crashTimes){ Timber.i(gson.toJson(crashTimes)); intcount=0; for(inti=crashTimes.size()-1;i>=0;i--){ longcrashTime=crashTimes.get(i); if(time-crashTime<=5*60*1000){ count++; if(count>=2){ break; } } } if(count>=2){ //在5分钟之内有三次闪退,这时候需要清空数据 returntrue; }else{ returnfalse; } } /** *清空数据,包括数据库中的和SharedPreferences中的 * *@throwsException */ privatevoidclearData()throwsException{ Timber.i("开始清理数据"); Files.deleteFilesExceptSomeInDirectory(mFileDir,FILES_DONTNEED_DELETE); } }
然后我们需要将CrashHandler添加到ACRA的异常处理Sender列表中。在你的Application类中添加如下代码。
@ReportsCrashes( //一些ACRA的设置,具体参考ACRA文档,因为我们使用自定义Sender,所以这里完全可以不用设置 //mailTo="bugs@treeholeapp.cn", //mode=ReportingInteractionMode.TOAST, //resToastText=R.string.crash_toast_text ) publicclassAppextendsApplication{ @Override publicvoidonCreate(){ if(!BuildConfig.DEBUG){//这里我判断只有在非DEBUG下才清除数据,主要是为了在开发过程中能够保留线程。 ACRA.init(APPLICATION_CONTEXT); CrashHandlerhandler=newCrashHandler(); ACRA.getErrorReporter().setReportSender(handler);//在闪退时检查是否要清空数据 } } }
总结
以上即为实现多次闪退后清除数据的实现,希望大家开发的AppBug越来越少。