Android使用CrashHandler捕獲全局異常

大家都知道饶米,現(xiàn)在安裝Android系統(tǒng)的手機(jī)版本和設(shè)備千差萬(wàn)別详瑞,在模擬器和自己的android手機(jī)上運(yùn)行良好的程序安裝到某款手機(jī)上說不定就出現(xiàn)崩潰的現(xiàn)象摘刑,或是本來好好的程序裝到領(lǐng)導(dǎo)或老板的手機(jī)上就崩潰了伟葫,這可是很尷尬很致命的裹芝,開發(fā)者個(gè)人不可能購(gòu)買所有設(shè)備逐個(gè)調(diào)試部逮,領(lǐng)導(dǎo)們也不可能把手機(jī)拿給你通過數(shù)據(jù)線進(jìn)行調(diào)試,所以在程序發(fā)布出去之后嫂易,如果出現(xiàn)了崩潰現(xiàn)象兄朋,開發(fā)者應(yīng)該及時(shí)獲取在該設(shè)備上導(dǎo)致崩潰的信息,這對(duì)于下一個(gè)版本的bug修復(fù)幫助極大怜械,所以今天就來介紹一下如何在程序崩潰的情況下收集相關(guān)的設(shè)備參數(shù)信息和具體的異常信息颅和,并發(fā)送這些信息到服務(wù)器供開發(fā)者分析和調(diào)試程序。

詳細(xì)操作:

1缕允、創(chuàng)建一個(gè)CrashHandler.java

importjava.io.File;

importjava.io.FileOutputStream;

importjava.io.PrintWriter;

importjava.io.StringWriter;

importjava.io.Writer;

importjava.lang.Thread.UncaughtExceptionHandler;

importjava.lang.reflect.Field;

importjava.text.DateFormat;

importjava.text.SimpleDateFormat;

importjava.util.Date;

importjava.util.HashMap;

importjava.util.Map;

importandroid.content.Context;

importandroid.content.pm.PackageInfo;

importandroid.content.pm.PackageManager;

importandroid.content.pm.PackageManager.NameNotFoundException;

importandroid.os.Build;

importandroid.os.Environment;

importandroid.os.Looper;

importandroid.util.Log;

importandroid.widget.Toast;

/**

*?UncaughtException處理類,當(dāng)程序發(fā)生Uncaught異常的時(shí)候,有該類來接管程序,并記錄發(fā)送錯(cuò)誤報(bào)告.

*/

public class CrashHandler implements UncaughtExceptionHandler?{

public static final String?TAG?="CrashHandler";

//系統(tǒng)默認(rèn)的UncaughtException處理類

private Thread.UncaughtExceptionHandler?mDefaultHandler;

//CrashHandler實(shí)例

privatestaticCrashHandler?INSTANCE?=newCrashHandler();

//程序的Context對(duì)象

private Context?mContext;

//用來存儲(chǔ)設(shè)備信息和異常信息

private Map?infos?=new HashMap();

//用于格式化日期,作為日志文件名的一部分

private Date Format?formatter?=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

/**?保證只有一個(gè)CrashHandler實(shí)例?*/

private CrashHandler()?{

}

/**?獲取CrashHandler實(shí)例?,單例模式?*/

public static CrashHandler?getInstance()?{

? ? ? ? ? ? return INSTANCE;

}

/**

*?初始化

*

*?@param?context

*/

public void init(Context?context)?{

? ? ? ? ? ? mContext?=?context;

? ? ? ? ? ? //獲取系統(tǒng)默認(rèn)的UncaughtException處理器

? ? ? ? ? ? mDefaultHandler?=?Thread.getDefaultUncaughtExceptionHandler();

? ? ? ? ? ? //設(shè)置該CrashHandler為程序的默認(rèn)處理器

? ? ? ? ? ? Thread.setDefaultUncaughtExceptionHandler(this);

}

/**

*?當(dāng)UncaughtException發(fā)生時(shí)會(huì)轉(zhuǎn)入該函數(shù)來處理

*/

@Override

public void uncaughtException(Thread?thread,?Throwable?ex)?{

? ? ? ? ? ? if(!handleException(ex)?&&?mDefaultHandler?!=null)?{

? ? ? ? ? ? ? ? ? ? ? ?//如果用戶沒有處理則讓系統(tǒng)默認(rèn)的異常處理器來處理

? ? ? ? ? ? ? ? ? ? ? ?mDefaultHandler.uncaughtException(thread,?ex);

? ? ? ? ? ? ?}else{

? ? ? ? ? ? ? ? ? ? ? ?try{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(3000);

? ? ? ? ? ? ? ? ? ? ? ? ?}catch(InterruptedException?e)?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Log.e(TAG,"error?:?",?e);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? //退出程序

? ? ? ? ? ? ? ? ? ? ? ? android.os.Process.killProcess(android.os.Process.myPid());

? ? ? ? ? ? ? ? ? ? ? ? System.exit(1);

? ? ? ? ? ? ? }

}

/**

*?自定義錯(cuò)誤處理,收集錯(cuò)誤信息?發(fā)送錯(cuò)誤報(bào)告等操作均在此完成.

*

*?@param?ex

*?@return?true:如果處理了該異常信息;否則返回false.

*/

private boolean handleException(Throwable?ex)?{

? ? ? ? ? ? ? if(ex?==null)?{

? ? ? ? ? ? ? ? ? ? ? ?return false;

? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? //使用Toast來顯示異常信息

? ? ? ? ? ? ? new Thread() {

? ? ? ? ? ? ? ? ? ? ? ?@Override

? ? ? ? ? ? ? ? ? ? ? ?public void run() {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Looper.prepare();

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Toast.makeText(mContext,"很抱歉,程序出現(xiàn)異常,即將退出.", Toast.LENGTH_LONG).show();

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Looper.loop();

? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ?}.start();

? ? ? ? ? ? ? ?//收集設(shè)備參數(shù)信息

? ? ? ? ? ? ? ?collectDeviceInfo(mContext);

? ? ? ? ? ? ? ?//保存日志文件

? ? ? ? ? ? ? saveCrashInfo2File(ex);

? ? ? ? ? ? ?return true;

}

/**

*?收集設(shè)備參數(shù)信息

*?@param?ctx

*/

public void collectDeviceInfo(Context?ctx)?{

? ? ? ? ? ? ?try{

? ? ? ? ? ? ? ? ? ? PackageManager?pm?=?ctx.getPackageManager();

? ? ? ? ? ? ? ? ? ? PackageInfo?pi?=?pm.getPackageInfo(ctx.getPackageName(),?PackageManager.GET_ACTIVITIES);

? ? ? ? ? ? ? ? ? ? if(pi?!=null)?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? String?versionName?=?pi.versionName?==null?"null":?pi.versionName;

? ? ? ? ? ? ? ? ? ? ? ? ? ? String?versionCode?=?pi.versionCode?+"";

? ? ? ? ? ? ? ? ? ? ? ? ? ? infos.put("versionName",?versionName);

? ? ? ? ? ? ? ? ? ? ? ? ? ? infos.put("versionCode",?versionCode);

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ?}catch(NameNotFoundException?e)?{

? ? ? ? ? ? ? ? ? ? ? Log.e(TAG,"an?error?occured?when?collect?package?info",?e);

? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? Field[]?fields?=?Build.class.getDeclaredFields();

? ? ? ? ? ? ? for(Field?field?:?fields)?{

? ? ? ? ? ? ? ? ? ? ? try{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?field.setAccessible(true);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?infos.put(field.getName(),?field.get(null).toString());

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.d(TAG,?field.getName()?+"?:?"+?field.get(null));

? ? ? ? ? ? ? ? ? ? ?}catch(Exception?e)?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.e(TAG,"an?error?occured?when?collect?crash?info",?e);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ?}

}

/**

*?保存錯(cuò)誤信息到文件中

*

*?@param?ex

*?@return??返回文件名稱,便于將文件傳送到服務(wù)器

*/

private String?saveCrashInfo2File(Throwable?ex)?{

? ? ? ? ? ? ? StringBuffer?sb?=newStringBuffer();

? ? ? ? ? ? ? for(Map.Entry?entry?:?infos.entrySet())?{

? ? ? ? ? ? ? ? ? ? ? ?String?key?=?entry.getKey();

? ? ? ? ? ? ? ? ? ? ? String?value?=?entry.getValue();

? ? ? ? ? ? ? ? ? ? ? sb.append(key?+"="+?value?+"\n");

? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ?Writer?writer?=new StringWriter();

? ? ? ? ? ? ? ?PrintWriter?printWriter?=new PrintWriter(writer);

? ? ? ? ? ? ? ?ex.printStackTrace(printWriter);

? ? ? ? ? ? ? ?Throwable?cause?=?ex.getCause();

? ? ? ? ? ? ? ?while(cause?!=null)?{

? ? ? ? ? ? ? ? ? ? ? ? ?cause.printStackTrace(printWriter);

? ? ? ? ? ? ? ? ? ? ? ? cause?=?cause.getCause();

? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ?printWriter.close();

? ? ? ? ? ? ? ?String?result?=?writer.toString();

? ? ? ? ? ? ? ?sb.append(result);

? ? ? ? ? ? ? ?try{

? ? ? ? ? ? ? ? ? ? ? ?long timestamp?=?System.currentTimeMillis();

? ? ? ? ? ? ? ? ? ? ? ?String?time?=?formatter.format(newDate());

? ? ? ? ? ? ? ? ? ? ? ?String?fileName?="crash-"+?time?+"-"+?timestamp?+".log";

? ? ? ? ? ? ? ? ? ? ? ?if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?String?path?="/sdcard/crash/";

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?File?dir?=new File(path);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(!dir.exists())?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?dir.mkdirs();

? ? ? ? ? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? ? ? ? ? ?FileOutputStream?fos?=new FileOutputStream(path?+?fileName);

? ? ? ? ? ? ? ? ? ? ? ? ?fos.write(sb.toString().getBytes());

? ? ? ? ? ? ? ? ? ? ? ? ?fos.close();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return fileName;

? ? ? ? ? ? ? ? }catch(Exception?e)?{

? ? ? ? ? ? ? ? ? ? ? ? ?Log.e(TAG,"an?error?occured?while?writing?file...",?e);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return null;

? ? ? ? ? }

}

在收集異常信息時(shí)峡扩,朋友們也可以使用Properties,因?yàn)镻roperties有一個(gè)很便捷的方法properties.store(OutputStream out, String comments)障本,用來將Properties實(shí)例中的鍵值對(duì)外輸?shù)捷敵隽髦杏卸睿窃谑褂玫倪^程中發(fā)現(xiàn)生成的文件中異常信息打印在同一行,看起來極為費(fèi)勁彼绷,所以換成Map來存放這些信息巍佑,然后生成文件時(shí)稍加了些操作。

2寄悯、完成這個(gè)CrashHandler后萤衰,我們需要在一個(gè)Application環(huán)境中讓其運(yùn)行,為此猜旬,我們繼承android.app.Application脆栋,在Application的onCreate()中加入以下兩行代碼,便可啟動(dòng)全局異常捕獲

//啟動(dòng)全局獲取異常

CrashHandler crashHandler = CrashHandler.getInstance();

crashHandler.init(getApplicationContext());

最后洒擦,為了讓我們的CrashApplication取代android.app.Application的地位椿争,在我們的代碼中生效,我們需要修改AndroidManifest.xml:

<application android:name="你的Application類名" />

因?yàn)槲覀僀rashHandler中熟嫩,遇到異常后要保存設(shè)備參數(shù)和具體異常信息到SDCARD秦踪,所以我們需要在AndroidManifest.xml中加入讀寫SDCARD權(quán)限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子椅邓,更是在濱河造成了極大的恐慌柠逞,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件景馁,死亡現(xiàn)場(chǎng)離奇詭異板壮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)合住,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門绰精,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人透葛,你說我怎么就攤上這事笨使。” “怎么了获洲?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵阱表,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我贡珊,道長(zhǎng)最爬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任门岔,我火速辦了婚禮爱致,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寒随。我一直安慰自己糠悯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布妻往。 她就那樣靜靜地躺著互艾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪讯泣。 梳的紋絲不亂的頭發(fā)上纫普,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音好渠,去河邊找鬼昨稼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拳锚,可吹牛的內(nèi)容都是我干的假栓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼霍掺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼匾荆!你這毒婦竟也來了拌蜘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤棋凳,失蹤者是張志新(化名)和其女友劉穎拦坠,沒想到半個(gè)月后连躏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剩岳,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年入热,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拍棕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勺良,死狀恐怖绰播,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尚困,我是刑警寧澤蠢箩,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站事甜,受9級(jí)特大地震影響谬泌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逻谦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一掌实、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邦马,春花似錦贱鼻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)究飞。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間星著,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工抒和, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抱冷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓添诉,卻偏偏與公主長(zhǎng)得像屁桑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栏赴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • /** * 1.全局捕獲異常類 * 2.@authorDell * 3.@date2017/9/19 17:03 ...
    天天大保建閱讀 216評(píng)論 0 0
  • 在Java 的異常處理機(jī)制中:1如果拋出的是Exception異常的話蘑斧,必須有try..catch..進(jìn)行處理,屬...
    吃貨養(yǎng)成記閱讀 1,092評(píng)論 0 1
  • 一、Android Crash說明 程序因未捕獲的異常而突然終止, 系統(tǒng)會(huì)調(diào)用處理程序的接口UncaughtExc...
    Mur閱讀 3,041評(píng)論 0 6
  • 對(duì)象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法竖瘾,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法沟突,并非Fa...
    孫小磊閱讀 1,986評(píng)論 0 3
  • 今天的幸運(yùn)兒童是聶雪和唐茜,胡靜文因?yàn)橛行┬″e(cuò)誤錯(cuò)過了成為幸運(yùn)兒的機(jī)會(huì)捕传。 中午也沒休息惠拭,加班加點(diǎn)把做了的練習(xí)冊(cè),黃...
    孫萬(wàn)群閱讀 220評(píng)論 0 0