1. 程式人生 > >Android異常處理框架

Android異常處理框架

           Android系統的“程式異常退出”,給應用的使用者體驗造成不良影響。為了捕獲應用執行時異常並給出友好提示,便可繼承UncaughtExceptionHandler類來處理。

思路:

        1.此類主要完成一下幾個任務

        1.1 提示友好的錯誤資訊

        1.1.1網路異常,錯誤碼:%d

        1.1.2網路異常,請求超時

        1.1.3網路異常,讀取資料超時

        1.1.4網路連線失敗,請檢查網路設定

        1.1.5資料解析異常

        1.1.6資料解析異常

        1.1.7檔案流異常

        1.1.8應用程式執行時異常

        1.2 儲存異常日誌

        1.2.1判斷是否掛載了SD卡

        1.2.2沒有掛載SD卡,無法寫檔案,直接return

        1.2.3將異常寫入檔案

1.3 自定義異常處理:收集錯誤資訊&傳送錯誤報告

        現在來研究一下 開源中國 異常處理框架。

/**
 * 應用程式異常類:用於捕獲異常和提示錯誤資訊
 * @author liux (http://my.oschina.net/liux)
 * @version 1.0
 * @created 2012-3-21
 */
public class AppException extends Exception implements UncaughtExceptionHandler{

	private final static boolean Debug = false;//是否儲存錯誤日誌
	
	/** 定義異常型別 */
	public final static byte TYPE_NETWORK 	= 0x01;
	public final static byte TYPE_SOCKET	= 0x02;
	public final static byte TYPE_HTTP_CODE	= 0x03;
	public final static byte TYPE_HTTP_ERROR= 0x04;
	public final static byte TYPE_XML	 	= 0x05;
	public final static byte TYPE_IO	 	= 0x06;
	public final static byte TYPE_RUN	 	= 0x07;
	public final static byte TYPE_JSON	 	= 0x08;
	
	private byte type;
	private int code;
	
	/** 系統預設的UncaughtException處理類 */
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	
	private AppException(){
		this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
	}
	
	private AppException(byte type, int code, Exception excp) {
		super(excp);
		this.type = type;
		this.code = code;		
		if(Debug){
			this.saveErrorLog(excp);
		}
	}
	public int getCode() {
		return this.code;
	}
	public int getType() {
		return this.type;
	}
	
	/**
	 * 提示友好的錯誤資訊
	 * @param ctx
	 */
	public void makeToast(Context ctx){
		switch(this.getType()){
		case TYPE_HTTP_CODE:
			String err = ctx.getString(R.string.http_status_code_error, this.getCode());
			Toast.makeText(ctx, err, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_HTTP_ERROR:
			Toast.makeText(ctx, R.string.http_exception_error, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_SOCKET:
			Toast.makeText(ctx, R.string.socket_exception_error, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_NETWORK:
			Toast.makeText(ctx, R.string.network_not_connected, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_XML:
			Toast.makeText(ctx, R.string.xml_parser_failed, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_JSON:
			Toast.makeText(ctx, R.string.xml_parser_failed, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_IO:
			Toast.makeText(ctx, R.string.io_exception_error, Toast.LENGTH_SHORT).show();
			break;
		case TYPE_RUN:
			Toast.makeText(ctx, R.string.app_run_code_error, Toast.LENGTH_SHORT).show();
			break;
		}
	}
	
	/**
	 * 儲存異常日誌
	 * @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;
			}
			//沒有掛載SD卡,無法寫檔案
			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) { }}
		}

	}
	
	public static AppException http(int code) {
		return new AppException(TYPE_HTTP_CODE, code, null);
	}
	
	public static AppException http(Exception e) {
		return new AppException(TYPE_HTTP_ERROR, 0 ,e);
	}

	public static AppException socket(Exception e) {
		return new AppException(TYPE_SOCKET, 0 ,e);
	}
	
	public static AppException io(Exception e) {
		if(e instanceof UnknownHostException || e instanceof ConnectException){
			return new AppException(TYPE_NETWORK, 0, e);
		}
		else if(e instanceof IOException){
			return new AppException(TYPE_IO, 0 ,e);
		}
		return run(e);
	}
	
	public static AppException xml(Exception e) {
		return new AppException(TYPE_XML, 0, e);
	}
	
	public static AppException json(Exception e) {
		return new AppException(TYPE_JSON, 0, e);
	}
	
	public static AppException network(Exception e) {
		if(e instanceof UnknownHostException || e instanceof ConnectException){
			return new AppException(TYPE_NETWORK, 0, e);
		}
		else if(e instanceof HttpException){
			return http(e);
		}
		else if(e instanceof SocketException){
			return socket(e);
		}
		return http(e);
	}
	
	public static AppException run(Exception e) {
		return new AppException(TYPE_RUN, 0, e);
	}

	/**
	 * 獲取APP異常崩潰處理物件
	 * @param context
	 * @return
	 */
	public static AppException getAppExceptionHandler(){
		return new AppException();
	}
	
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {

		if(!handleException(ex) && mDefaultHandler != null) {
			mDefaultHandler.uncaughtException(thread, ex);
		}

	}
	/**
	 * 自定義異常處理:收集錯誤資訊&傳送錯誤報告
	 * @param ex
	 * @return true:處理了該異常資訊;否則返回false
	 */
	private boolean handleException(Throwable ex) {
		if(ex == null) {
			return false;
		}
		
		final Context context = AppManager.getAppManager().currentActivity();
		
		if(context == null) {
			return false;
		}
		
		final String crashReport = getCrashReport(context, ex);
		//顯示異常資訊&傳送報告
		new Thread() {
			public void run() {
				Looper.prepare();
				UIHelper.sendAppCrashReport(context, crashReport);
				Looper.loop();
			}

		}.start();
		return true;
	}
	/**
	 * 獲取APP崩潰異常報告
	 * @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();
	}
}
使用:

1.在Application子類,註冊App異常崩潰處理器。

  application是用來儲存全域性變數的,並且是在package建立的時候就跟著存在了。

  Thread.setDefaultUncaughtExceptionHandler()方法將異常處理類設定到執行緒上。

	@Override
	public void onCreate() {
		super.onCreate();
        Thread.setDefaultUncaughtExceptionHandler(AppException.getAppExceptionHandler());

	}

2. 例:檢查更新版本
	/**
	 * 檢查版本更新
	 * @param url
	 * @return
	 */
	public static Update checkVersion(AppContext appContext) throws AppException {
		try{
			return Update.parse(http_get(appContext, URLs.UPDATE_VERSION));		
		}catch(Exception e){
			if(e instanceof AppException)
				throw (AppException)e;
			throw AppException.network(e);
		}
	}


public static Update parse(InputStream inputStream) throws IOException, AppException {
		Update update = null;

        try {        	
      		//.......
        } catch (XmlPullParserException e) {
			throw AppException.xml(e);
        } finally {
        	inputStream.close();	
        }      
        return update;       
}

UpdateManager.java

new Thread(){
			public void run() {
				Message msg = new Message();
				try {					
					Update update = ApiClient.checkVersion((AppContext)mContext.getApplicationContext());
					msg.what = 1;
					msg.obj = update;
				} catch (AppException e) {
					e.printStackTrace();
				}
				handler.sendMessage(msg);
			}			
		}.start();	

3.普通異常處理
    /**
     * 下載圖片-可指定顯示圖片的高寬
     * @param url
     * @param width
     * @param height
     */
    private Bitmap downloadBitmap(String url, int width, int height) {   
        Bitmap bitmap = null;
        try {
			//http載入圖片
			bitmap = ApiClient.getNetBitmap(url);
			if(width > 0 && height > 0) {
				//指定顯示圖片的高寬
				bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
			} 
			//放入快取
			cache.put(url, new SoftReference<Bitmap>(bitmap));
		} catch (AppException e) {
			e.printStackTrace();
		}
        return bitmap;  
    }  

4.傳送toast訊息
			final Handler handler = new Handler(){
				public void handleMessage(Message msg) {
					if(mProgress!=null)mProgress.dismiss();
					if(msg.what == 1){
						Result res = (Result)msg.obj;
						UIHelper.ToastMessage(QuestionPub.this, res.getErrorMessage());
						if(res.OK()){
							//傳送通知廣播
							if(res.getNotice() != null){
								UIHelper.sendBroadCast(QuestionPub.this, res.getNotice());
							}
							//清除之前儲存的編輯內容
							ac.removeProperty(AppConfig.TEMP_POST_TITLE,AppConfig.TEMP_POST_CATALOG,AppConfig.TEMP_POST_CONTENT);
							//跳轉到文章詳情
							finish();
						}
					}
					else {
						((AppException)msg.obj).makeToast(QuestionPub.this);
					}
				}
			};
			new Thread(){
				public void run() {
					Message msg = new Message();					
					try {
						Result res = ac.pubPost(post);
						msg.what = 1;
						msg.obj = res;
		            } catch (AppException e) {
		            	e.printStackTrace();
						msg.what = -1;
						msg.obj = e;
		            }
					handler.sendMessage(msg);
				}
			}.start();

((AppException)msg.obj).makeToast(QuestionPub.this);     

msg.obj為Exception ,強制轉換到AppException,得到異常型別,Toast.

/**
	 * 傳送App異常崩潰報告
	 * 
	 * @param cont
	 * @param crashReport
	 */
	public static void sendAppCrashReport(final Context cont,
			final String crashReport) {
		AlertDialog.Builder builder = new AlertDialog.Builder(cont);
		builder.setIcon(android.R.drawable.ic_dialog_info);
		builder.setTitle(R.string.app_error);
		builder.setMessage(R.string.app_error_message);
		builder.setPositiveButton(R.string.submit_report,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 傳送異常報告
						Intent i = new Intent(Intent.ACTION_SEND);
						// i.setType("text/plain"); //模擬器
						i.setType("message/rfc822"); // 真機
						i.putExtra(Intent.EXTRA_EMAIL,
								new String[] { "[email protected]" });
						i.putExtra(Intent.EXTRA_SUBJECT,
								"開源中國Android客戶端 - 錯誤報告");
						i.putExtra(Intent.EXTRA_TEXT, crashReport);
						cont.startActivity(Intent.createChooser(i, "傳送錯誤報告"));
						// 退出
						AppManager.getAppManager().AppExit(cont);
					}
				});
		builder.setNegativeButton(R.string.sure,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 退出
						AppManager.getAppManager().AppExit(cont);
					}
				});
		builder.show();
	}


相關推薦

Android異常處理框架

           Android系統的“程式異常退出”,給應用的使用者體驗造成不良影響。為了捕獲應用執行時異常並給出友好提示,便可繼承UncaughtExceptionHandler類來處理。 思路:         1.此類主要完成一下幾個任務         1.1

Android程式崩潰異常處理框架

目前我已經把框架放在了github了,地址如下:https://github.com/msdx/android-crash 使用方法見github上面的README.md。 本專案相關jar包已釋出在jcenter,如果你使用gradle構建工具,可以直接新增依賴,具體見

TakePhoto-輕量級Android照片處理框架

TakePhotoV2.0已釋出,詳見《TakePhoto-輕量級Android照片處理框架V2.0釋出》 TakePhoto 簡介 支援以拍照的方式獲取照片 支援從相簿選擇照片 支援對照片進行裁切 支援對照片進行壓縮 支援對裁切及壓縮引數自定義 支援因

React Native for Android 異常處理概覽

本文的RN程式碼基於0.43版本   https://blog.desmondyao.com/rn-crash/ 準備接入React Native(RN)時,看看前輩們分享的經驗,都說剛接入時崩潰率是一個問題。最近在做RN的Native部分優化,今天就來聊聊在RN中的異常都是什麼,該怎麼處理。 前言

異常處理框架(一)

1、新建 【HloException.java】 package com.test.exception; public class HLoException extends Exception{ static final long serialVer

在java Spring基礎上實現自定義異常處理框架教程

應用專案大致的體系結構:         該異常處理框架滿足的要求: 完整的異常組織結構異常的統一處理可配置,受管式,方便使用 完整的異常組織結構: 使用者可以方便的定義自己的異常,但所有UncheckedException需要繼承BaseAppRuntimeExce

Android異常處理——UncaughtExceptionHandler捕獲全域性異常

  Android系統的“程式異常退出”,給應用的使用者體驗造成不良影響。為了捕獲應用執行時異常並給出友好提示,便可繼承UncaughtExceptionHandler類來處理。通過Thread.setDefaultUncaughtExceptionHandler()方法將

在pring基礎上實現自定義異常處理框架教程

應用專案大致的體系結構:         該異常處理框架滿足的要求: 完整的異常組織結構異常的統一處理可配置,受管式,方便使用 完整的異常組織結構: 使用者可以方便的定義自己的異常,但所有UncheckedException

android異常處理概述

而Java提供了一套比較優秀的異常處理機制:      1、使開發人員不必編寫特殊程式碼來測試返回值就能發現問題,      2、在語法結構就把正常的程式碼和異常處理的程式碼清晰的分開來,      3、允許我們使用相同的異常處理程式碼來處理一定範圍內的所有異常。  以期

【邊做項目邊學Android異常處理android.os.NetworkOnMainThreadException--多線程問題

不能 timeout throws extend dex com order trace res 一切搞定。以為高枕無憂了,結果還是有問題! log開始報錯了,獲取更新信息異常。。!debug一下。發現Exception:android.os.NetworkOnM

基於QProbe創建基本Android圖像處理框架

基本 nbsp lin eight clas 識別 應該 edi pan 先來看一個GIF這個GIF中有以下幾個值得註意的地方這個界面是基本的主要界面所應該在的地方。其右下角有一個“+”號,點擊後,打開圖像采集界面在這個界面最上面的地方,顯示的是當前圖像處理的狀態。(一般來

Android第六課 安裝異常處理

manager ems sources man ade known and lib 裏程碑 1 已安裝了存在簽名沖突的同名數據包 通過軟件管理,將即將安裝的XXX.apk的同名軟件卸載,然後進入到安裝包中,點擊XXX.apk2 拷貝文件夾失敗 打開設置,選擇存儲

Java框架-SpringMVC統一異常處理、ssm框架整合

1. SpringMVC中異常處理 1.1 各層處理異常原則即實現方法 1.1.1 各層處理異常原則 dao:不處理,拋異常; service:不處理,拋異常; controller/servlet:必須處理,否則錯誤資訊將直接在瀏覽器顯示給使用者看。 1.1.2 異

關於除錯android時eclipse裝置連線異常處理

此方法適用於很多情況,如: 1、當eclipse和AS同開時,通常會有這種情況出現,這時候可以使用此方法; 2、eclipse run as時發現裝置欄有許多無用裝置,通常是emulated-xx,可以使用此方法關掉。 方法: 1、開啟命令列(開始-->在搜尋框輸入cmd

android sdk manager 更新異常 處理方法

  一   修改hosts   ,     127.0.0.1 localhost #Google主頁 203.208.46.146 www.goog

任務排程框架quartz使用總結(異常處理,解決恢復後多次排程處理)

任務排程框架quartz使用總結(異常處理,解決恢復後多次排程處理)      首先先說說什麼是排程框架,大白話所謂的排程框架你可以把它看成一個定時任務管理框架,並且quartz框架是多執行緒的, quartz最主要的三大基本特性: (1)排程器&nbs

Spring Boot 全域性異常處理 與 Hibernate Validator校驗框架整合

Hibernate Validator校驗框架的使用 Spring boot已經集成了hibernate-validator,不需要引入maven,其他框架也可以自己引入: <dependency> <groupId>org.h

Android java.lang.UnsupportedOperationException以及佈局異常處理

           專案中出現 java.lang.UnsupportedOperationException然後報出佈局檔案異常,即android.view.InflateException:

基於spring框架的java開發中的異常處理

在springmvc框架的中異常處理的方式有兩種: 1,在控制器中使用@ExceptionHandler(xxxException.class)註解修飾一個方法,該註解能夠處理通一個控制器類中的丟擲的xxxExcepiton異常。 使用控制器通知註解@ControllerAdvice

我的第一個python web開發框架(40)——後臺日誌與異常處理

1 #!/usr/bin/env python 2 # coding=utf-8 3 4 from bottle import put 5 from common import web_helper, encrypt_helper, security_helper 6