Android實現(xiàn)多次閃退清除數(shù)據(jù)
很多時候由于后臺返回的數(shù)據(jù)異常,可能會導(dǎo)致App閃退。而如果這些異常數(shù)據(jù)被App本地緩存下來,那么即使殺掉進(jìn)程重新進(jìn)入還是會發(fā)生閃退。唯一的解決方法就是清除App數(shù)據(jù),但是用戶可能沒有這個意識或者嫌麻煩就直接不再使用了,這是我們無法接受的。在使用淘寶、追書神器等App時我發(fā)現(xiàn)有時候它們也會連續(xù)閃退,但是往往閃退三次后就恢復(fù)正常了,所以一般成熟的App都會做連續(xù)閃退三次后清除緩存數(shù)據(jù)的工作。而目前筆者搜不到有哪篇blog來講這方面的事情,所以就姑且由我來講講此事,為希望提高App用戶體驗的朋友提供些許參考。
ACRA
為了能夠在閃退的時候做一些事情,我們可以使用ACRA,這是Github上的一個開源項目,允許使用者設(shè)置一些Sender在App閃退的時候做一些事情。具體使用可以直接參考Github。如果不希望使用ACRA,那么也可以自己實現(xiàn)一個UncachedExceptionHandler并替換系統(tǒng)默認(rèn)的Handler,并在這個Handler里面對數(shù)據(jù)進(jìn)行處理。
實現(xiàn)清除數(shù)據(jù)
ACRA提供了自己的一些Sender,如使用系統(tǒng)郵件客戶端向指定郵箱發(fā)送郵件的EmailIntentSender。而我們希望記錄閃退次數(shù)和清除數(shù)據(jù)則需要implements ReportSender接口。
public class CrashHandler implements ReportSender { @Override public void send(Context context, CrashReportData errorContent) throws ReportSenderException { Timber.i("閃退,檢查是否需要清空數(shù)據(jù)"); new CrashModel().checkAndClearData(); } }
這里我們寫了一個CrashModel用來記錄閃退次數(shù)和時間決定是否需要清空數(shù)據(jù),具體代碼如下。 由于在ReportSender的時候無法打開其它線程,所以我們無法使用SharedPerferences來清理數(shù)據(jù)(打開SP的時候其實打開了一個新線程)。為此需要找到數(shù)據(jù)緩存的位置并將文件刪除。同樣道理,記錄閃退時間也只能通過文件記錄。當(dāng)然,你可以選擇一些文件不進(jìn)行刪除,如用戶信息等不太容易出問題的數(shù)據(jù)。
public class CrashModel { private static final String KEY_CRASH_TIMES = "crash_times"; private static final String CRASH_TIME_FILE_NAME = "crash_time"; //不能通過App.getPackageName來獲取包名,否則會有問題,只能默認(rèn)為cn.campusapp.campus。所以對于debug或者運營版本,清數(shù)據(jù)會把release的清掉 private static final String FILE_DIR = String.format("/data/data/%s/", BuildConfig.APPLICATION_ID); private static final String ACCOUNT_FILE_NAME = String.format("%s%s", FILE_DIR, "shared_prefs/account_pref.xml"); private static ArrayList<String> FILES_DONTNEED_DELETE = new ArrayList<>(); //該目錄中的文件不會被刪除 static { FILES_DONTNEED_DELETE.add(ACCOUNT_FILE_NAME); //目前賬號信息文件不會被刪除,但是會手動改變數(shù)據(jù),只保留userId accessToken 和school } protected ArrayList<Long> mCrashTimes; Gson gson = new Gson(); private File mFileDir; public CrashModel() { mFileDir = new File(FILE_DIR); mCrashTimes = readCrashTimes(); if (mCrashTimes == null) { mCrashTimes = new ArrayList<>(); storeCrashTimes(mCrashTimes); } } public void checkAndClearData() { long timeNow = System.currentTimeMillis(); if (checkClearData(timeNow, new ArrayList<>(mCrashTimes))) { Timber.i("已經(jīng)在5分鐘之內(nèi)有三次閃退,需要清理數(shù)據(jù)"); try { clearData(); } catch (Exception e) { Timber.e(e, "清空所有數(shù)據(jù)失敗"); } } else { mCrashTimes.add(timeNow); storeCrashTimes(mCrashTimes); Timber.i("此次不需要清空數(shù)據(jù), %s", gson.toJson(mCrashTimes)); } } private void storeCrashTimes(ArrayList<Long> crashTimes) { try { String str = gson.toJson(crashTimes); Files.writeToFile(mFileDir, CRASH_TIME_FILE_NAME, str); } catch (Exception e) { Timber.e(e, "保存閃退時間失敗"); } } private ArrayList<Long> readCrashTimes() { try { String timeStr = Files.readFileContent(mFileDir, CRASH_TIME_FILE_NAME); return gson.fromJson(timeStr, new TypeToken<ArrayList<Long>>() { }.getType()); } catch (Exception e) { Timber.e(e, "讀取閃退時間失敗"); } return null; } /** * 檢查是否需要清空數(shù)據(jù),目前的清空策略是在5分鐘之內(nèi)有三次閃退的就清空數(shù)據(jù),也就是從后往前遍歷,只要前兩次閃退發(fā)生在5分鐘之內(nèi),就清空數(shù)據(jù) * * @return */ private boolean checkClearData(long time, ArrayList<Long> crashTimes) { Timber.i(gson.toJson(crashTimes)); int count = 0; for (int i = crashTimes.size() - 1; i >= 0; i--) { long crashTime = crashTimes.get(i); if (time - crashTime <= 5 * 60 * 1000) { count++; if (count >= 2) { break; } } } if (count >= 2) { //在5分鐘之內(nèi)有三次閃退,這時候需要清空數(shù)據(jù) return true; } else { return false; } } /** * 清空數(shù)據(jù),包括數(shù)據(jù)庫中的和SharedPreferences中的 * * @throws Exception */ private void clearData() throws Exception { Timber.i("開始清理數(shù)據(jù)"); Files.deleteFilesExceptSomeInDirectory(mFileDir, FILES_DONTNEED_DELETE); } }
然后我們需要將CrashHandler 添加到ACRA的異常處理Sender列表中。在你的Application類中添加如下代碼。
@ReportsCrashes( //一些ACRA的設(shè)置,具體參考ACRA文檔,因為我們使用自定義Sender,所以這里完全可以不用設(shè)置 //mailTo = "bugs@treeholeapp.cn", //mode = ReportingInteractionMode.TOAST, //resToastText = R.string.crash_toast_text ) public class App extends Application { @Override public void onCreate() { if (!BuildConfig.DEBUG) { //這里我判斷只有在非DEBUG下才清除數(shù)據(jù),主要是為了在開發(fā)過程中能夠保留線程。 ACRA.init(APPLICATION_CONTEXT); CrashHandler handler = new CrashHandler(); ACRA.getErrorReporter().setReportSender(handler); //在閃退時檢查是否要清空數(shù)據(jù) } } }
總結(jié)
以上即為實現(xiàn)多次閃退后清除數(shù)據(jù)的實現(xiàn),希望大家開發(fā)的App Bug越來越少。
相關(guān)文章
Android網(wǎng)絡(luò)編程之簡易新聞客戶端
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)絡(luò)編程之簡易新聞客戶端的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05一行代碼教你解決Scrollview和TextInput焦點獲取問題
這篇文章主要為大家介紹了一行代碼教你解決Scrollview和TextInput焦點獲取問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android UI自定義ListView實現(xiàn)下拉刷新和加載更多效果
這篇文章主要介紹了Android UI自定義ListView實現(xiàn)下拉刷新和加載更多效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11Android入門教程之RecyclerView的具體使用詳解
RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實現(xiàn)數(shù)據(jù)縱向滾動,也可以實現(xiàn)橫向滾動(ListView做不到橫向滾動)。接下來講解RecyclerView的用法2021-10-10android中創(chuàng)建通知欄Notification代碼實例
這篇文章主要介紹了android中創(chuàng)建通知欄Notification代碼實例,本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-05-05Android使用Notification實現(xiàn)通知功能
這篇文章主要為大家詳細(xì)介紹了Android使用Notification實現(xiàn)通知功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11Android簡單實現(xiàn)無限滾動自動滾動的ViewPager
這篇文章主要介紹了Android簡單實現(xiàn)無限滾動自動滾動的ViewPager,百度谷歌上面也有很多關(guān)于這方面的教程,但是感覺都略顯麻煩,而且封裝的都不是很徹底。所以試著封裝一個比較好用的ViewPager,實現(xiàn)思路一起通過本文學(xué)習(xí)吧2016-12-12