Android實(shí)現(xiàn)多次閃退清除數(shù)據(jù)
很多時(shí)候由于后臺(tái)返回的數(shù)據(jù)異常,可能會(huì)導(dǎo)致App閃退。而如果這些異常數(shù)據(jù)被App本地緩存下來,那么即使殺掉進(jìn)程重新進(jìn)入還是會(huì)發(fā)生閃退。唯一的解決方法就是清除App數(shù)據(jù),但是用戶可能沒有這個(gè)意識(shí)或者嫌麻煩就直接不再使用了,這是我們無法接受的。在使用淘寶、追書神器等App時(shí)我發(fā)現(xiàn)有時(shí)候它們也會(huì)連續(xù)閃退,但是往往閃退三次后就恢復(fù)正常了,所以一般成熟的App都會(huì)做連續(xù)閃退三次后清除緩存數(shù)據(jù)的工作。而目前筆者搜不到有哪篇blog來講這方面的事情,所以就姑且由我來講講此事,為希望提高App用戶體驗(yàn)的朋友提供些許參考。
ACRA
為了能夠在閃退的時(shí)候做一些事情,我們可以使用ACRA,這是Github上的一個(gè)開源項(xiàng)目,允許使用者設(shè)置一些Sender在App閃退的時(shí)候做一些事情。具體使用可以直接參考Github。如果不希望使用ACRA,那么也可以自己實(shí)現(xiàn)一個(gè)UncachedExceptionHandler并替換系統(tǒng)默認(rèn)的Handler,并在這個(gè)Handler里面對(duì)數(shù)據(jù)進(jìn)行處理。
實(shí)現(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();
}
}
這里我們寫了一個(gè)CrashModel用來記錄閃退次數(shù)和時(shí)間決定是否需要清空數(shù)據(jù),具體代碼如下。 由于在ReportSender的時(shí)候無法打開其它線程,所以我們無法使用SharedPerferences來清理數(shù)據(jù)(打開SP的時(shí)候其實(shí)打開了一個(gè)新線程)。為此需要找到數(shù)據(jù)緩存的位置并將文件刪除。同樣道理,記錄閃退時(shí)間也只能通過文件記錄。當(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來獲取包名,否則會(huì)有問題,只能默認(rèn)為cn.campusapp.campus。所以對(duì)于debug或者運(yùn)營版本,清數(shù)據(jù)會(huì)把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<>(); //該目錄中的文件不會(huì)被刪除
static {
FILES_DONTNEED_DELETE.add(ACCOUNT_FILE_NAME); //目前賬號(hào)信息文件不會(huì)被刪除,但是會(huì)手動(dòng)改變數(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, "保存閃退時(shí)間失敗");
}
}
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, "讀取閃退時(shí)間失敗");
}
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í)候需要清空數(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文檔,因?yàn)槲覀兪褂米远xSender,所以這里完全可以不用設(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í)檢查是否要清空數(shù)據(jù)
}
}
}
總結(jié)
以上即為實(shí)現(xiàn)多次閃退后清除數(shù)據(jù)的實(shí)現(xiàn),希望大家開發(fā)的App Bug越來越少。
相關(guān)文章
Android編程判斷當(dāng)前應(yīng)用是否在后臺(tái)運(yùn)行的方法示例
這篇文章主要介紹了Android編程判斷當(dāng)前應(yīng)用是否在后臺(tái)運(yùn)行的方法,涉及Android針對(duì)當(dāng)前程序運(yùn)行狀態(tài)相關(guān)屬性操作與判定技巧,需要的朋友可以參考下2018-03-03
Android Http協(xié)議訪問網(wǎng)絡(luò)實(shí)例(3種)
本篇文章主要介紹了Android Http協(xié)議訪問網(wǎng)絡(luò)實(shí)例(3種),具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
Android開發(fā)實(shí)現(xiàn)調(diào)節(jié)屏幕亮度功能
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)調(diào)節(jié)屏幕亮度功能,涉及Android權(quán)限控制及屏幕亮度相關(guān)屬性操作技巧,需要的朋友可以參考下2018-03-03
Flutter中嵌入Android 原生TextView實(shí)例教程
這篇文章主要給大家介紹了關(guān)于Flutter中嵌入Android 原生TextView的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
漂亮的Android音樂歌詞控件 仿網(wǎng)易云音樂滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了漂亮的Android音樂歌詞控件,仿網(wǎng)易云音樂滑動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Android studio中生成引用.aar和.jar的方法詳解
這篇文章主要是講解.aar的生成與引用,文中的內(nèi)容屬于完全基礎(chǔ)性概念,對(duì)剛學(xué)習(xí)使用Android studio的朋友們很有幫助,有需要的可以參考學(xué)習(xí),下面來一起看看吧。2016-09-09
Android開發(fā)中如何去掉app標(biāo)題欄的實(shí)現(xiàn)
這篇文章主要介紹了Android開發(fā)中如何去掉app標(biāo)題欄的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04

