標籤:java
開始本博客以前,請先閱讀:
Retrofit請求數據對錯誤以及網絡異常的處理android
實際開發常常有這種狀況,好比登陸請求,接口返回的
信息包括請求返回的狀態:失敗仍是成功,錯誤碼,User對象等等。若是網絡等緣由引發的登陸失敗能夠歸結爲異常,若是是用戶信息輸入錯誤致使的登陸失敗算是錯誤。json
假如服務器返回的是統一數據格式:服務器
/** * 標準數據格式 * @param <T> */ public class Response<T> { public int state; public String message; public T data; }
不管是異常仍是錯誤,都要在subscribe裏面處理異常信息,以下代碼:markdown
APIWrapper.getInstance().login("username", "password") .subscribe(new Observer<Response<User>>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Response<User> data) { if(data.state == 1001){ //..... }else if(data.state == 1002){ } } });
如今我但願在發生任何錯誤的狀況下,都會調用onError事件,而且由model來處理錯誤信息。那麼,此時咱們就應該有一個ExceptionEngine來處理事件流中的錯誤信息了。網絡
在Retrofit+RxJava的框架下,咱們獲取網絡數據的流程一般以下:app
訂閱->請求接口->數據解析->更新UI框架
整個數據請求過程都是發生在Rx中的工做流之中。當有異常產生的時候,咱們要儘可能不在ui層裏面進行判斷,換句話說,咱們沒有必要去告訴ui層具體的錯誤信息,只須要讓他彈出一個信息(Toast或者Dialog)展現咱們給它的信息就行。ide
請求接口和數據解析均可能出錯,因此在這兩層進行錯誤處理。爲了更好的解耦,咱們經過攔截器攔截錯誤,而後根據錯誤類型分發信息。ui
這個攔截器主要是爲了獲取具體的錯誤信息,分發給上層的UI,給用戶以提示,加強用戶體驗。
public Observable<Weather> getWeather(String cityName){ return weatherService.getWeather(cityName) //攔截服務器返回的錯誤 .map(new ServerResponseFunc<Weather>()) //HttpResultFunc()爲攔截onError事件的攔截器,後面會講到,這裏先忽略 .onErrorResumeNext(new HttpResponseFunc<Weather>()); }
//攔截固定格式的公共數據類型Response<T>,判斷裏面的狀態碼 private class ServerResponseFunc<T> implements Func1<Response<T>, T> { @Override public T call(Response<T> reponse) { //對返回碼進行判斷,若是不是0,則證實服務器端返回錯誤信息了,便根據跟服務器約定好的錯誤碼去解析異常 if (reponse.state != 0) { //若是服務器端有錯誤信息返回,那麼拋出異常,讓下面的方法去捕獲異常作統一處理 throw new ServerException(reponse.state,reponse.message); } //服務器請求數據成功,返回裏面的數據實體 return reponse.data; } }
因此整個邏輯是這樣的:
因此在前三步的過程當中,只要發生異常(服務器返回的錯誤也拋出了)都會拋出,這時候就觸發了RxJava的OnError事件。
這個攔截器主要是將異常信息轉化爲用戶」能看懂」的友好提示。
private class HttpResponseFunc<T> implements Func1<Throwable, Observable<T>> { @Override public Observable<T> call(Throwable throwable) { //ExceptionEngine爲處理異常的驅動器 return Observable.error(ExceptionEngine.handleException(throwable)); } }
兩個攔截器之前使用,代碼以下:
public Observable<Weather> getWeather(String cityName){ return weatherService.getWeather(cityName) //攔截服務器返回的錯誤 .map(new ServerResponseFunc<Weather>()) //HttpResponseFunc()爲攔截onError事件的攔截器 .onErrorResumeNext(new HttpResponseFunc<Weather>()); }
相關類:
public class RxSubscriber<T> extends ErrorSubscriber<T> { @Override public void onStart() { super.onStart(); DialogHelper.showProgressDlg(context, "正在加載數據"); } @Override public void onCompleted() { DialogHelper.stopProgressDlg(); } @Override protected void onError(ApiException ex) { DialogHelper.stopProgressDlg(); Toast.makeText(context, ex.message, Toast.LENGTH_SHORT).show(); } @Override public void onNext(T t) { } } public abstract class ErrorSubscriber<T> extends Subscriber<T> { @Override public void onError(Throwable e) { if(e instanceof ApiException){ onError((ApiException)e); }else{ onError(new ApiException(e,123)); } } /** * 錯誤回調 */ protected abstract void onError(ApiException ex); }
處理異常的驅動器
package com.sanniuben.net; import android.net.ParseException; import com.google.gson.JsonParseException; import org.json.JSONException; import java.net.ConnectException; import retrofit2.adapter.rxjava.HttpException; /** * Created by Lzx on 2016/7/11. */ public class ExceptionEngine { //對應HTTP的狀態碼 private static final int UNAUTHORIZED = 401; private static final int FORBIDDEN = 403; private static final int NOT_FOUND = 404; private static final int REQUEST_TIMEOUT = 408; private static final int INTERNAL_SERVER_ERROR = 500; private static final int BAD_GATEWAY = 502; private static final int SERVICE_UNAVAILABLE = 503; private static final int GATEWAY_TIMEOUT = 504; public static ApiException handleException(Throwable e){ ApiException ex; if (e instanceof HttpException){ //HTTP錯誤 HttpException httpException = (HttpException) e; ex = new ApiException(e, ERROR.HTTP_ERROR); switch(httpException.code()){ case UNAUTHORIZED: case FORBIDDEN: case NOT_FOUND: case REQUEST_TIMEOUT: case GATEWAY_TIMEOUT: case INTERNAL_SERVER_ERROR: case BAD_GATEWAY: case SERVICE_UNAVAILABLE: default: ex.message = "網絡錯誤"; //均視爲網絡錯誤 break; } return ex; } else if (e instanceof ServerException){ //服務器返回的錯誤 ServerException resultException = (ServerException) e; ex = new ApiException(resultException, resultException.code); ex.message = resultException.message; return ex; } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException){ ex = new ApiException(e, ERROR.PARSE_ERROR); ex.message = "解析錯誤"; //均視爲解析錯誤 return ex; }else if(e instanceof ConnectException){ ex = new ApiException(e, ERROR.NETWORD_ERROR); ex.message = "鏈接失敗"; //均視爲網絡錯誤 return ex; }else { ex = new ApiException(e, ERROR.UNKNOWN); ex.message = "未知錯誤"; //未知錯誤 return ex; } } } /** * 約定異常 */ public class ERROR { /** * 未知錯誤 */ public static final int UNKNOWN = 1000; /** * 解析錯誤 */ public static final int PARSE_ERROR = 1001; /** * 網絡錯誤 */ public static final int NETWORD_ERROR = 1002; /** * 協議出錯 */ public static final int HTTP_ERROR = 1003; }
public class ApiException extends Exception { public int code; public String message; public ApiException(Throwable throwable, int code) { super(throwable); this.code = code; } } public class ServerException extends RuntimeException { public int code; public String message; }
DialogHelper.java
public class DialogHelper { /** * 通用Dialog * */ // 由於本類不是activity因此經過繼承接口的方法獲取到點擊的事件 public interface OnOkClickListener { abstract void onOkClick(); } /** * Listener */ public interface OnCancelClickListener { abstract void onCancelClick(); } private static AlertDialog mDialog; public static void showDialog(Context context, String title, String content, final OnOkClickListener listenerYes, final OnCancelClickListener listenerNo) { showDialog(context, context.getString(android.R.string.ok), context.getString(android.R.string.cancel), title, content, listenerYes, listenerNo); } public static void showDialog(Context context, String ok, String cancel, String title, String content, final OnOkClickListener listenerYes, final OnCancelClickListener listenerNo) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setMessage(content); // 設置title builder.setTitle(title); // 設置肯定按鈕,固定用法聲明一個按鈕用這個setPositiveButton builder.setPositiveButton(ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // 若是肯定被電擊 if (listenerYes != null) { listenerYes.onOkClick(); } mDialog = null; } }); // 設置取消按鈕,固定用法聲明第二個按鈕要用setNegativeButton builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // 若是取消被點擊 if (listenerNo != null) { listenerNo.onCancelClick(); } mDialog = null; } }); // 控制這個dialog可不能夠按返回鍵,true爲能夠,false爲不能夠 builder.setCancelable(false); // 顯示dialog mDialog = builder.create(); if (!mDialog.isShowing()) mDialog.show(); } public static void showDialog(Context context, int ok, int cancel, int title, int content, final OnOkClickListener listenerYes, final OnCancelClickListener listenerNo) { showDialog(context, context.getString(ok), context.getString(cancel), context.getString(title), context.getString(content), listenerYes, listenerNo); } static ProgressDialog progressDlg = null; /** * 啓動進度條 * * @param strMessage 進度條顯示的信息 * @param // 當前的activity */ public static void showProgressDlg(Context ctx, String strMessage) { if (null == progressDlg) { if (ctx == null) return; progressDlg = new ProgressDialog(ctx); //設置進度條樣式 progressDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); //提示的消息 progressDlg.setMessage(strMessage); progressDlg.setIndeterminate(false); progressDlg.setCancelable(true); progressDlg.show(); } } public static void showProgressDlg(Context ctx) { showProgressDlg(ctx, ""); } /** * 結束進度條 */ public static void stopProgressDlg() { if (null != progressDlg && progressDlg.isShowing()) { progressDlg.dismiss(); progressDlg = null; } if (null != dialog && dialog.isShowing()) { dialog.dismiss(); dialog = null; } } private static Dialog dialog; public static void showDialogForLoading(Context context, String msg, boolean cancelable) { if (null == dialog) { if (null == context) return; View view = LayoutInflater.from(context).inflate(R.layout.layout_loading_dialog, null); TextView loadingText = (TextView)view.findViewById(R.id.loading_tip_text); loadingText.setText(msg); dialog = new Dialog(context, R.style.loading_dialog_style); dialog.setCancelable(cancelable); dialog.setCanceledOnTouchOutside(cancelable); dialog.setContentView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); Activity activity = (Activity) context; if (activity.isFinishing()) return; dialog.show(); } } }