【Android】利用 ACRA 實現在規定時間內崩潰次數超過規定值就自動清理 APP 資料
其實 ACRA 是一個崩潰處理的類庫,其功能就是收集App崩潰堆疊資訊,生成報告併發送到指定端,當然它也可以自己定製對應的操作,所以是個處理崩潰很不錯的庫。
ACRA Application Crash Reports for Android
其實在規定時間內崩潰次數超過規定值就自動清理 APP 資料這個功能在一些大型APP上都會有,因為這對於解決某些因本地資料導致的崩潰有很好的作用,使用者沒必要再進行解除安裝重灌,算是一個細節加分。
本文實現的是清理 APP 自身 data 目錄下的資料以及 SharedPreferences 裡面的資料,如需清理其它目錄下的資料請參考程式碼進行修改。
下面就說說怎麼去實現:
首先我們先在 build.gradle 裡面新增依賴:
dependencies {
...
compile 'ch.acra:acra:4.7.0'
...
}
這裡要宣告一下為什麼要用4.7.0,寫這篇文章的時候最新的版本是4.9.2,其實幾個版本都試過,4.7.0最合我心意,因為這個版本在 Application 初始化資料崩潰也會被收集,4.7.0之後就不行,所以為了多一層保障而選擇了4.7.0。
接下來是自定義 Application:
@ReportsCrashes( //一些ACRA的設定,具體參考ACRA文件,因為我們使用自定義Sender,所以這裡完全可以不用設定 // mailTo = "
[email protected]", // mode = ReportingInteractionMode.TOAST, // resToastText = R.string.crash_toast_text ) public class MyApplication extends Application { private static Context context; @Override public void onCreate() { initACRA(); super.onCreate(); context = getApplicationContext(); } public void initACRA() { if (!BuildConfig.DEBUG) { //這裡判斷只有在非DEBUG下才清除資料,主要是為了在開發過程中能夠保留執行緒。 ACRA.init(this); CrashHandler handler = new CrashHandler(); ACRA.getErrorReporter().setReportSender(handler); //在閃退時檢查是否要清空資料 } } public static Context getContext(){ return context; } }
然後在 AndroidManifest.xml 裡面設定 Application:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="...">
<application
...
android:name="xxx.MyApplication">
...
</application>
</manifest>
接下來就是自定義 ReportSender 類的程式碼:
public class CrashHandler implements ReportSender {
@Override
public void send(Context context, CrashReportData errorContent) throws ReportSenderException {
//閃退,檢查是否需要清空資料
new CrashModel().checkAndClearData();
}
}
然後是崩潰處理 CrashModel 類的程式碼:
public class CrashModel {
private static final String CRASH_TIME_FILE_NAME = "crash_time";
//不能通過App.getPackageName來獲取包名,否則會有問題,只能預設為cn.campusapp.campus。所以對於debug或者運營版本,清資料會把release的清掉
private static final String FILE_DIR = String.format("/data/data/%s/", BuildConfig.APPLICATION_ID);
protected ArrayList<Long> mCrashTimes;
Gson gson = new Gson();
public CrashModel() {
mCrashTimes = readCrashTimes();
if (mCrashTimes == null) {
mCrashTimes = new ArrayList<>();
storeCrashTimes(mCrashTimes);
}
}
public void checkAndClearData() {
long timeNow = System.currentTimeMillis();
if (checkClearData(timeNow, new ArrayList<>(mCrashTimes))) {
//已經在5分鐘之內有三次閃退,需要清理資料
try {
clearData();
}
catch (Exception e) {
//清空所有資料失敗
}
}
else {
mCrashTimes.add(timeNow);
storeCrashTimes(mCrashTimes);
//此次不需要清空資料, 崩潰此時:gson.toJson(mCrashTimes));
}
}
private void storeCrashTimes(ArrayList<Long> crashTimes) {
try {
String str = gson.toJson(crashTimes);
FileUtil.writeToFile(FILE_DIR + CRASH_TIME_FILE_NAME, str);
}
catch (Exception e) {
//儲存閃退時間失敗
}
}
private ArrayList<Long> readCrashTimes() {
try {
String timeStr = FileUtil.readFileContent(FILE_DIR + CRASH_TIME_FILE_NAME);
return gson.fromJson(timeStr, new TypeToken<ArrayList<Long>>() {
}.getType());
}
catch (Exception e) {
//讀取閃退時間失敗
}
return null;
}
/**
* 檢查是否需要清空資料,目前的清空策略是在5分鐘之內有三次閃退的就清空資料,也就是從後往前遍歷,只要前兩次閃退發生在5分鐘之內,就清空資料
*
* @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分鐘之內有三次閃退,這時候需要清空資料
return true;
} else {
return false;
}
}
/**
* 清空資料,包括資料庫中的和SharedPreferences中的
*
* @throws Exception
*/
private void clearData() throws Exception {
//開始清理資料
FileUtil.delFolderNew(FILE_DIR);
SharedPreUtil.getInstance().Clear();
}
}
這裡需要用到 Gson ,請從 GitHub 獲取 jar:
然後還有一個檔案處理類 FileUtil:
public class FileUtil {
/**
* Prints some data to a file using a BufferedWriter
*/
public static boolean writeToFile(String filename, String data) {
BufferedWriter bufferedWriter = null;
try {
// Construct the BufferedWriter object
bufferedWriter = new BufferedWriter(new FileWriter(filename));
// Start writing to the output stream
bufferedWriter.write(data);
return true;
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
// Close the BufferedWriter
try {
if (bufferedWriter != null) {
bufferedWriter.flush();
bufferedWriter.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return false;
}
/**
* 讀檔案,並返回String
* @param strFileName 檔名
*/
public static String readFileContent(String strFileName) throws IOException {
File file = new File(strFileName);
return readFileContentAsString(file, null);
}
public static String readFileContentAsString(File file, String charsetName) throws IOException {
if (!file.exists() || !file.isFile()) {
throw new IOException("File to be readed not exist, file path : " + file.getAbsolutePath());
}
FileInputStream fileIn = null;
InputStreamReader inReader = null;
BufferedReader bReader = null;
try {
fileIn = new FileInputStream(file);
inReader = charsetName == null ? new InputStreamReader(fileIn) : new InputStreamReader(fileIn, charsetName);
bReader = new BufferedReader(inReader);
StringBuffer content = new StringBuffer();
char[] chBuffer = new char[1024];
int readedNum = -1;
while ((readedNum = bReader.read(chBuffer)) != -1) {
content.append(chBuffer, 0, readedNum);
}
return content.toString();
} finally {
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException e) {
}
}
if (bReader != null) {
try {
bReader.close();
} catch (IOException e) {
}
}
}
}
/**
* 刪除資料夾(調整後的)
*
* @param folderPath
* String 資料夾路徑及名稱 如c:/fqf
*/
public static void delFolderNew(String folderPath) {
try {
delAllFileNew(folderPath); // 刪除完裡面所有內容
File myFilePath = new File(folderPath);
myFilePath.delete(); // 刪除空資料夾
}
catch (Exception e) {
System.out.println("刪除資料夾操作出錯");
e.printStackTrace();
}
}
/**
* 刪除資料夾裡面的所有檔案(調整後的)
* @param path String 資料夾路徑 如 c:/fqf
*/
public static void delAllFileNew(String path) {
File file = new File(path);
if (!file.exists()) {
return;
}
if (!file.isDirectory()) {
return;
}
String[] tempList = file.list();
File temp = null;
String tempPath = "";
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
tempPath = path + tempList[i];
}
else {
temp = new File(path + File.separator + tempList[i]);
tempPath = path + File.separator + tempList[i];
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
delFolderNew(tempPath);// 先刪除資料夾裡面的檔案
}
}
}
}
(檔案處理工具類很實用,這裡只展示了要用到的一部分,可以自己再豐富其方法。)
最後還有一個處理 SharedPreferences 資料的工具類:
public class SharedPreUtil{
private SharedPreUtil() {}
private static SharedPreUtil sharedpreutil;
public static SharedPreUtil getInstance() {
if (sharedpreutil == null) {
synchronized (SharedPreUtil.class) {
sharedpreutil = new SharedPreUtil();
}
}
return sharedpreutil;
}
public static void clear(){
SharedPreferences prefer = MyApplication.getContext().getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = prefers.edit();
editor.remove("key1");
editor.remove("key2");
editor.remove("key3");
}
}
其思路就是通過 MyApplication 獲得 context,然後獲取 SharedPreferences,再進行資料處理。
這裡只是對崩潰次數進行判斷以及本地資料清理處理,如果想進行上報崩潰記錄等操作,可以再自行研究下 ACRA 這個庫。
相關推薦
【Android】利用 ACRA 實現在規定時間內崩潰次數超過規定值就自動清理 APP 資料
其實 ACRA 是一個崩潰處理的類庫,其功能就是收集App崩潰堆疊資訊,生成報告併發送到指定端,當然它也可以自己定製對應的操作,所以是個處理崩潰很不錯的庫。 ACRA Application Crash Reports for Android 其實在規定時間內崩潰次數超
【jQuery】利用jQuery實現“記住我”的功能
jquer sms sep jquery實現 .com script lis put bar 【1】先下載jQuery.cookie插件:使用幫助請參考鏈接(https://github.com/carhartl/jquery-cookie)。 【2】安裝插件:
【 MATLAB 】用 MATLAB 實現離散時間傅立葉變換(DTFT)的兩個案例分析
先給出離散時間傅立葉變換的簡單介紹: 如果 x(n) 是絕對可加的,即 那麼它的離散時間傅立葉變換給出為: w 稱為數字頻率,單位是每樣本 rad(弧度)或 (弧度/樣本)(rad/sam
【Qt】利用QAxObject實現word轉pdf
通過QAxObject類操作office的com元件操作word,呼叫word的介面儲存為pdf,所以必須安裝了office才能用。 下面先貼程式碼再做說明 QAxObject *pWordApplication = new QAxObject("Word.Appli
【前端】利用ajax實現偽檔案非同步上傳下載
利用ajax可以實現很酷的效果,在不重新整理頁面的情況下提交表單、修改資料狀態等等,可是如果表單裡還有input:file可就慘了,ajax不支援檔案的處理啊! ajax是使用了瀏覽器內部的XmlHttpRequest物件來傳輸XML資料的。既然是Xml的資料傳輸,那麼傳輸
【android】利用BuildConfig.DEBUG來控制日誌的輸出
前言 在Android 應用程式開發中,不可避免地會常常輸出各種除錯資訊,通常我們會使用android.util.Log類輸出該類日誌資訊(這是推薦使用的方式)。然而,在專案釋出的時候,我們常常需要關閉這些冗餘的Log資訊,手工關閉Log相當不方
【WCF】利用WCF實現上傳下載檔案服務
引言 前段時間,用WCF做了一個小專案,其中涉及到檔案的上傳下載。出於複習鞏固的目的,今天簡單梳理了一下,整理出來,下面展示如何一步步實現一個上傳下載的WCF服務。 服務端 1.首先新建一個名為FileService的WCF服務庫專案,如下圖:
【Android】 FragmentTabHost+Fragment實現多標籤頁
Android TabHost多標籤頁幾乎是所有APP中必備的控制元件,如通迅錄的【撥號、通話記錄、聯絡人】,微信的【聊天、聯絡人、發現】,如下圖 Android API13之後官方就不推薦使用TabActivity了,取而代之的是FragmentActivity+F
【探索】利用 canvas 實現資料壓縮
前言 HTTP 支援 GZip 壓縮,可節省不少傳輸資源。但遺憾的是,只有下載才有,上傳並不支援。如果上傳也能壓縮,那就完美了。特別適合大量文字提交的場合,比如部落格園,就是很好的例子。 雖然標準不支援「上傳壓縮」,但仍可以自己來實現。 Flash 首選方案當然是 Flash,畢竟它提供了壓縮 API。除了
【Linux】利用訊號實現sleep函式
在另一篇文章Linux訊號中,介紹了訊號的產生與處理方式,以及一系列訊號集函式的使用。 本文使用訊號機制,模擬實現sleep函式並瞭解競態條件。 在此之前先介紹一波需要用到的函式。 sigaction函式 #include <signal.h>
【轉載】vue.js實現格式化時間並每秒更新顯示功能示例
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user
【IOS】利用ASIHTTPRequest 實現一個簡單的登陸驗證
【原創作品, 歡迎轉載,轉載請在明顯處註明! 謝謝。 今天給大家帶來一個簡單的登陸驗證,用的是ASIHttpRequest 這個開源類庫,使用的方法很簡單,從網上下載下來以後,新增到專案中,並新增一下這些框架。 下面上程式碼 // // ViewControll
【Android】使用BaseAdapter實現複雜的ListView
步驟 使用BaseAdapter實現複雜的ListView的步驟: 1. 資料你要準備好 List getData()。 2. 繼承ListActivity專有屏,不再需要setContentView(xxx)。 3. 建立一個繼承自Ba
【Android】用RecycleView實現可以橫向滾動的ListView效果
終於閒下來了,總結一下RecycleView的使用。 一、概述 與常見的ListView和GridView一樣,RecycleView也用來在有限的介面上展示大量的資料。它提供了一種插拔式的體驗,高度的解耦,使用非常靈活,可以通過support-v7包進行匯入。先看以下Re
【Android】動態載入實現的簡單demo
概念▪說明動態載入:此處的動態載入是指從服務端或者其他地方獲取jar包,並在執行時期,載入jar包,並與 jar包互相呼叫。本例中,為了方便演示,將要動態載入的jar放到了assets目錄下,在程式執行時期,將其載入到/data/data/pkgname/files下,來模擬
【Redis】利用 Redis 實現分散式鎖
## 技術背景 首先我們需要先來了解下什麼是分散式鎖,以及為什麼需要分散式鎖。 對於這個問題,我們可以簡單將鎖分為兩種——記憶體級鎖以及分散式鎖,記憶體級鎖即我們在 Java 中的 synchronized 關鍵字(或許加上程序級鎖修飾更恰當些),而分散式鎖則是應用在分散式系統中的一種鎖機制。分散式鎖的應
【Java】 劍指offer(39) 陣列中出現次數超過一半的數字 《劍指Offer》Java實現合集 《劍指Offer》Java實現合集
本文參考自《劍指offer》一書,程式碼採用Java語言。 更多:《劍指Offer》Java實現合集 題目 陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1, 2, 3, 2, 2, 2, 5, 4, 2}。由於數字2在陣列中出現
【Android】Android聊天機器人實現
小米 div bottom 曾經 圖靈 .9.png sdn http 歡迎界面 昨天看到一個Android視頻教程講圖靈機器人。那個API接口用起來還是挺方便的,就準備自己動手做一個了。另外自己還使用了高德地圖的API接口用於定位(曾經用過高德的接口,比X度方便) 大
【Android】實現線程異步小技巧
使用 msg xtend util rri wsh ride 執行 java 方式不止一種,這裏使用的是Timer類,創建一個定時器。我們經常需要獲得移動設備端口的顯示屏信息,但是onCreate()方法執行的時候,OnShow()方法不一定執行了,也就是說,在執行Oncr
【Android】如何實現Android發送短信
ted param close ase find array 短信 red phone 第一種:調用系統短信接口直接發送短信;主要代碼如下: /** * 直接調用短信接口發短信 * @param phoneNumber * @