Android使用CrashHandler來獲取應(yīng)用的crash信息的方法
在日常開發(fā)的過程中應(yīng)該不可避免的會(huì)發(fā)生 crash,無論你的程序?qū)懙亩嗝赐昝溃疾豢赡芡耆苊?crash 的發(fā)生,可能是由于 Android 底層的 bug,也可能是由于不充分的機(jī)型適配或者是糟糕的網(wǎng)絡(luò)狀況。當(dāng) crash 發(fā)生時(shí),系統(tǒng)就會(huì)kill掉正在執(zhí)行的程序,現(xiàn)象就是閃退,或者提醒用戶程序已經(jīng)停止運(yùn)行,這對(duì)用戶來說是很不友好的,也是我們不愿意看到的,更早的是當(dāng)用戶發(fā)生 crash,我們開發(fā)者卻無法得知程序?yàn)楹?crash,即便我們想去解決這個(gè) bug,但是由于無法知道用戶當(dāng)時(shí)的 crash 信息,所以往往也無能為力,幸運(yùn)的是,Andorid 提供了處理這類問題的方法,接下來我們就來一起看看到底 Android 給我們提供了什么方法來解決這個(gè)棘手的問題
一、Thread 類中的 setDefaultUncaughtExceptionHandler
/** * Sets the default uncaught exception handler. This handler is invoked in * case any Thread dies due to an unhandled exception. * * @param handler * The handler to set or null. */ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { Thread.defaultUncaughtHandler = handler; }
這個(gè)方法其實(shí)就可以解決我們應(yīng)用程序的 crash 問題,設(shè)置系統(tǒng)默認(rèn)異常處理器,當(dāng)系統(tǒng)發(fā)生crash 時(shí),系統(tǒng)就會(huì)回調(diào) UncaughtExceptionHandler 的 uncaughtException 方法,在 uncaughtException 方法中就可以獲取到異常信息,可以選擇把異常信息存儲(chǔ)下來,存儲(chǔ)方式大家可以自己選擇,然后在合適的時(shí)候通過網(wǎng)絡(luò)將 crash 信息上傳到服務(wù)器上,這樣我們開發(fā)人員就可以分析用戶 crash 的場(chǎng)景從而在后面的版本中進(jìn)行修復(fù),我們還可以在發(fā)生 crash 發(fā)生時(shí)彈出一個(gè)對(duì)話框,告訴用戶程序 crash 了,然后再退出
二、實(shí)現(xiàn)自己的異常捕獲類
1)建立異常 Handler,命名為 CrashHandler,代碼如下
/** * 異常捕獲類 * Created by qiudengjiao on 2017/9/29. */ public class CrashHandler implements Thread.UncaughtExceptionHandler { private static final String TAG = "CrashHandler"; private static final boolean DEBUG = true; private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/ryg_test/log/"; private static final String FILE_NAME = "crash"; //log文件的后綴名 private static final String FILE_NAME_SUFFIX = ".trace"; private static CrashHandler sInstance = new CrashHandler(); //系統(tǒng)默認(rèn)的異常處理(默認(rèn)情況下,系統(tǒng)會(huì)終止當(dāng)前的異常程序) private Thread.UncaughtExceptionHandler mDefaultCrashHandler; private Context mContext; //構(gòu)造方法私有,防止外部構(gòu)造多個(gè)實(shí)例 private CrashHandler() { } public static CrashHandler getInstance() { return sInstance; } /** * 初始化 * * @param context */ public void init(Context context) { //獲取系統(tǒng)默認(rèn)的異常處理器 mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); //將當(dāng)前實(shí)例設(shè)為系統(tǒng)默認(rèn)的異常處理器 Thread.setDefaultUncaughtExceptionHandler(this); //獲取Context,方便內(nèi)部使用 mContext = context.getApplicationContext(); } /** * 這個(gè)是最關(guān)鍵的函數(shù),當(dāng)程序中有未被捕獲的異常,系統(tǒng)將會(huì)自動(dòng)調(diào)用#uncaughtException方法 * thread為出現(xiàn)未捕獲異常的線程,ex為未捕獲的異常,有了這個(gè)throwable,我們就可以得到異常信息 * * @param thread * @param throwable */ @Override public void uncaughtException(Thread thread, Throwable throwable) { try { //導(dǎo)出異常信息到SD卡中 dumpExceptionToSDCard(throwable); //這里可以通過網(wǎng)絡(luò)上傳異常信息到服務(wù)器,便于開發(fā)人員分析日志從而解決bug uploadExceptionToServer(); } catch (IOException e) { e.printStackTrace(); } //打印出當(dāng)前調(diào)用棧信息 throwable.printStackTrace(); //如果系統(tǒng)提供了默認(rèn)的異常處理器,則交給系統(tǒng)去結(jié)束我們的程序,否則就由我們自己結(jié)束自己 if (mDefaultCrashHandler != null) { mDefaultCrashHandler.uncaughtException(thread, throwable); } else { android.os.Process.killProcess(android.os.Process.myPid()); } } /** * 保存到內(nèi)存卡 * 這里我們也可以根據(jù)項(xiàng)目需要選擇其他的保存方式 * * @param throwable * @throws IOException */ private void dumpExceptionToSDCard(Throwable throwable) throws IOException { //如果SD卡不存在或無法使用,則無法把異常信息寫入SD卡 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (DEBUG) { Log.w(TAG, "sdcard unmounted,skip dump exception"); return; } } File dir = new File(PATH); if (!dir.exists()) { dir.mkdirs(); } long current = System.currentTimeMillis(); String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current)); //以當(dāng)前時(shí)間創(chuàng)建log文件 File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX); try { PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); //導(dǎo)出發(fā)生異常的時(shí)間 pw.println(time); //導(dǎo)出手機(jī)信息 dumpPhoneInfo(pw); pw.println(); //導(dǎo)出異常的調(diào)用棧信息 throwable.printStackTrace(pw); pw.close(); } catch (Exception e) { Log.e(TAG, "dump crash info failed"); } } /** * 收集設(shè)備參數(shù)信息 * * @param pw * @throws PackageManager.NameNotFoundException */ private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException { //應(yīng)用的版本名稱和版本號(hào) PackageManager pm = mContext.getPackageManager(); PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); pw.print("App Version: "); pw.print(pi.versionName); pw.print('_'); pw.println(pi.versionCode); //android版本號(hào) pw.print("OS Version: "); pw.print(Build.VERSION.RELEASE); pw.print("_"); pw.println(Build.VERSION.SDK_INT); //手機(jī)制造商 pw.print("Vendor: "); pw.println(Build.MANUFACTURER); //手機(jī)型號(hào) pw.print("Model: "); pw.println(Build.MODEL); //cpu架構(gòu) pw.print("CPU ABI: "); pw.println(Build.CPU_ABI); } /** * 將異常信息上傳到服務(wù)器 */ private void uploadExceptionToServer() { //在這里寫上傳到服務(wù)器的邏輯 } }
從上面的代碼可以看出,當(dāng)應(yīng)用程序崩潰時(shí),CrashHandler 類會(huì)將異常信息以及設(shè)備信息寫入 SD 卡,這里大家也可以根據(jù)自己項(xiàng)目需要進(jìn)行處理,例如也可以存儲(chǔ)在數(shù)據(jù)庫(kù)中,接著將異常交給系統(tǒng)處理,系統(tǒng)會(huì)幫我們中止程序,如果系統(tǒng)沒有默認(rèn)的異常處理機(jī)制,那么就自行中止,當(dāng)然而又可以選擇將異常信息上傳到服務(wù)器,這里我們沒有實(shí)現(xiàn)這個(gè)邏輯,實(shí)際開發(fā)中都需要將異常信息上傳到服務(wù)器
三、如何使用 CrashHandler
其實(shí)使用 CrashHandler 也非常簡(jiǎn)單,我們可以在 Application 初始化的時(shí)候來設(shè)置 CrashHandler,如下所示:
/** * 自定義 Application 類 * Created by qiudengjiao on 2017/9/29. */ public class App extends Application { @Override public void onCreate() { super.onCreate(); init(); } private void init() { //初始化異常捕獲類 CrashHandler CrashHandler.getInstance().init(this); } }
通過上面的操作,我們的程序就能捕獲到 crash 了,同時(shí)還能從服務(wù)器上查看用戶的 crash 信息,今天就寫到這里,給大家推薦一本不錯(cuò)的書:Android 開發(fā)藝術(shù),作者是任玉剛,相信大家也都知道,這本書的內(nèi)容還是非常不錯(cuò)的,值得大家一看,比較適合有一定 Android 基礎(chǔ)的同學(xué),馬上就是國(guó)慶小長(zhǎng)假了,祝大家國(guó)慶節(jié)愉快
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ScrollView與SeekBar綁定實(shí)現(xiàn)滑動(dòng)時(shí)出現(xiàn)小滑塊效果
這篇文章主要為大家詳細(xì)介紹了ScrollView與SeekBar綁定實(shí)現(xiàn)滑動(dòng)時(shí)出現(xiàn)小滑塊效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Android實(shí)現(xiàn)房貸計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)房貸計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Android實(shí)現(xiàn)氣泡布局/彈窗效果 氣泡尖角方向及偏移量可控
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)氣泡布局/彈窗效果,可控制氣泡尖角方向及偏移量,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Android開發(fā)筆記SQLite優(yōu)化記住密碼功能
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)筆記SQLite優(yōu)化記住密碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Android進(jìn)程間通信(IPC)機(jī)制Binder簡(jiǎn)要介紹
本文主要介紹 Android進(jìn)程間通信(IPC)機(jī)制Binder簡(jiǎn)要介紹, 這里介紹了Binder機(jī)制如何實(shí)現(xiàn)進(jìn)程通信機(jī)制,有研究Android源碼的朋友可以看下2016-08-08Android開發(fā)之利用Activity實(shí)現(xiàn)Dialog對(duì)話框
這篇文章主要給大家介紹了Android開發(fā)之如何利用Activity實(shí)現(xiàn)Dialog對(duì)話框效果,文中給出了詳細(xì)的示例代碼,相信對(duì)大家的理解及學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們下面來一起看看吧。2016-12-12解決Android studio模擬器啟動(dòng)失敗的問題
這篇文章主要介紹了Android studio模擬器啟動(dòng)失敗的問題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android開發(fā)使用HttpURLConnection進(jìn)行網(wǎng)絡(luò)編程詳解【附源碼下載】
這篇文章主要介紹了Android開發(fā)使用HttpURLConnection進(jìn)行網(wǎng)絡(luò)編程的方法,結(jié)合實(shí)例形式分析了Android基于HttpURLConnection實(shí)現(xiàn)顯示圖片與文本功能,涉及Android布局、文本解析、數(shù)據(jù)傳輸、權(quán)限控制等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01Android用注解與反射實(shí)現(xiàn)Butterknife功能
Butterknife是一個(gè)在android上實(shí)現(xiàn)ioc(控制反轉(zhuǎn))的一個(gè)庫(kù)。ioc的核心是解耦。解耦的目的是修改耦合對(duì)象時(shí)不影響另外一個(gè)對(duì)象,降低模塊之間的關(guān)聯(lián)。在Spring中ioc更多的是依靠xml的配置。而android上的IOC框架可以不使用xml配置2022-11-11