原 薦 Android開(kāi)源中國(guó)客戶端學(xué)習(xí) 異常處理模塊 <12>
發(fā)表于1 年 前(2013-09-09 18:01) 閱讀( 745) | 評(píng)論( 5) 56人收藏此文章, 我要收藏
贊1
不得不說(shuō)Osc的客戶端的一些模塊和商業(yè)系統(tǒng)很接近,這也是值得我們學(xué)習(xí)的地方。
osc的異常處理模塊就是這樣的一個(gè)模塊,雖然個(gè)人認(rèn)為功能有些耦合,但是不得不說(shuō)還是很有參考價(jià)值的。
OSC的異常處理模塊分為兩大功能:
1.程序崩潰處理
由于種種原因,我們并不能保證我們的應(yīng)用可以在所有手機(jī)上正常運(yùn)行,開(kāi)發(fā)過(guò)程中開(kāi)發(fā)中印象最深的一個(gè)dialog也應(yīng)該是”XXX已經(jīng)停止運(yùn)行“吧?雖然在ICS以后android的崩潰提示已經(jīng)比較人性化,我們不能從中知曉是哪里出錯(cuò)。所以崩潰日志對(duì)于我們開(kāi)發(fā)者就顯得非常重要。
OSC在應(yīng)用崩潰后會(huì)生成崩潰日志并提示用戶把日志以email發(fā)送。雖然email發(fā)送這步可能沒(méi)多少人做,但是我們既然拿到了日志,也就可以自行在自己的應(yīng)用中將其發(fā)送到后臺(tái)了,現(xiàn)在市場(chǎng)很多app都有崩潰日志上傳這個(gè)功能。
osc崩潰處理的時(shí)序圖如下 注意不要忘了在androidmanifest中注冊(cè)AppContext
分為兩步:
a.注冊(cè)崩潰處理,不讓系統(tǒng)自己處理崩潰而讓我們的app來(lái)處理
其實(shí)很簡(jiǎn)單在AppContext的onCreate中:
1
2
3
4
5
6
7
8
|
@Override
public void onCreate() {
super .onCreate();
//注冊(cè)App異常崩潰處理器
Thread.setDefaultUncaughtExceptionHandler(AppException.getAppExceptionHandler());
init();
}
|
setDefaultUncaughtExceptionHandler函數(shù)的作用:
Sets the default uncaught exception handler. This handler is invoked in case any Thread dies due to an unhandled exception.
其實(shí)就是如果這線程崩潰了就會(huì)調(diào)用這個(gè)handler的uncaughtException函數(shù)
b 自己處理崩潰
如果應(yīng)用一旦崩潰就會(huì)執(zhí)行:
1
2
3
4
5
6
7
8
9
|
2 .這個(gè)函數(shù)中會(huì)獲取崩潰日志并顯示dialog提示用戶上傳
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null ) {
mDefaultHandler.uncaughtException(thread, ex);
}
}
|
然后就會(huì)生成崩潰日志并顯示dialog 提示用戶通過(guò)email把崩潰日志發(fā)送
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/**
* 自定義異常處理:收集錯(cuò)誤信息&發(fā)送錯(cuò)誤報(bào)告
* @param ex
* @return true:處理了該異常信息;否則返回false
*/
private boolean handleException(Throwable ex) {
if (ex == null ) {
return false ;
}
final Context context = AppManager.getAppManager().currentActivity(); //為了獲取 應(yīng)用的版本
if (context == null ) {
return false ;
}
final String crashReport = getCrashReport(context, ex);
//顯示異常信息&發(fā)送報(bào)告
new Thread() {
public void run() {
Looper.prepare();
UIHelper.sendAppCrashReport(context, crashReport);
Looper.loop();
}
}.start();
return true ;
}
|
生成崩潰日志代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
獲取日志代碼
/**
* 獲取APP崩潰異常報(bào)告
* @param ex
* @return
*/
private String getCrashReport(Context context, Throwable ex) {
PackageInfo pinfo = ((AppContext)context.getApplicationContext()).getPackageInfo();
StringBuffer exceptionStr = new StringBuffer();
exceptionStr.append( "Version: " +pinfo.versionName+ "(" +pinfo.versionCode+ ")\n" );
exceptionStr.append( "Android: " +android.os.Build.VERSION.RELEASE+ "(" +android.os.Build.MODEL+ ")\n" );
exceptionStr.append( "Exception: " +ex.getMessage()+ "\n" );
StackTraceElement[] elements = ex.getStackTrace();
for ( int i = 0 ; i < elements.length; i++) {
exceptionStr.append(elements[i].toString()+ "\n" );
}
return exceptionStr.toString();
}
|
2.應(yīng)用運(yùn)行中的非崩潰異常處理(比如網(wǎng)絡(luò)異常的捕獲)
個(gè)人感覺(jué)這個(gè)功能不應(yīng)該在AppException中處理,但是確實(shí)這些錯(cuò)誤應(yīng)該在一個(gè)類(可以稱為非崩潰異常中心)中處理。
以獲取圖片的異常為例:
ApiClient.getNetBitmap()
函數(shù)中有
1
2
3
|
if (statusCode != HttpStatus.SC_OK) {
throw AppException.http(statusCode);
}
|
一句,如果返回code不是200就拋出一個(gè)異常:注意雖然還是在AppException中進(jìn)行處理的但是確實(shí)和上面的崩潰沒(méi)多少關(guān)系
1
2
3
|
public static AppException http( int code) {
return new AppException(TYPE_HTTP_CODE, code, null );
}
|
看看這個(gè)構(gòu)造函數(shù)
1
2
3
4
5
6
7
8
|
private AppException( byte type, int code, Exception excp) {
super (excp);
this .type = type;
this .code = code;
if (Debug){
this .saveErrorLog(excp);
}
}
|
這里就把log保存到本地了 適時(shí)上傳到服務(wù)器就可以分析這些異常了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* 保存異常日志
* @param excp
*/
public void saveErrorLog(Exception excp) {
String errorlog = "errorlog.txt" ;
String savePath = "" ;
String logFilePath = "" ;
FileWriter fw = null ;
PrintWriter pw = null ;
try {
//判斷是否掛載了SD卡
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)){
savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSChina/Log/" ;
File file = new File(savePath);
if (!file.exists()){
file.mkdirs();
}
logFilePath = savePath + errorlog;
}
//沒(méi)有掛載SD卡,無(wú)法寫文件
if (logFilePath == "" ){
return ;
}
File logFile = new File(logFilePath);
if (!logFile.exists()) {
logFile.createNewFile();
}
fw = new FileWriter(logFile, true );
pw = new PrintWriter(fw);
pw.println( "--------------------" +( new Date().toLocaleString())+ "---------------------" );
excp.printStackTrace(pw);
pw.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pw != null ){ pw.close(); }
if (fw != null ){ try { fw.close(); } catch (IOException e) { }}
}
}
|
|