Retrofit+RxJava 优雅的处理服务器返回异常、错误

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                      

开始本博客之前,请先阅读:
Retrofit请求数据对错误以及网络异常的处理 

异常&错误

实际开发经常有这种情况,比如登录请求,接口返回的
信息包括请求返回的状态:失败还是成功,错误码,User对象等等。如果网络等原因引起的登录失败可以归结为异常,如果是用户信息输入错误导致的登录失败算是错误。

假如服务器返回的是统一数据格式:

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
/** * 标准数据格式 * @param <T> */public class Response<T> {    public int state;    public String message;    public T data;}
  • 网络异常导致的登录失败,在使用Retrofit+RxJava请求时都会直接调用subscribe的onError事件;
  • 密码错误导致的登录失败,在使用Retrofit+RxJava请求时都会调用subscribe的onNext事件;

无论是异常还是错误,都要在subscribe里面处理异常信息,如下代码:

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
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来处理事件流中的错误信息了。

在工作流中处理异常

在正常情况下,我们获取网络数据的流程通常如下:

请求接口->解析数据->更新UI

整个数据请求过程都是发生在Rx中的工作流之中。当有异常产生的时候,我们要尽量不在ui层里面进行判断,换句话说,我们没有必要去告诉ui层具体的错误信息,只需要让他弹出一个信息(Toast或者Dialog)展示我们给它的信息就行。

请求接口和数据解析都可能出错,所以在这两层进行错误处理。为了更好的解耦,我们通过拦截器拦截错误,然后根据错误类型分发信息。

拦截器

数据解析层的拦截器

这个拦截器主要是为了获取具体的错误信息,分发给上层的UI,给用户以提示,增强用户体验。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public Observable<Weather> getWeather(String cityName){        return weatherService.getWeather(cityName)                //拦截服务器返回的错误                .map(new ServerResponseFunc<Weather>())                //HttpResultFunc()为拦截onError事件的拦截器,后面会讲到,这里先忽略                .onErrorResumeNext(new HttpResponseFunc<Weather>());    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
//拦截固定格式的公共数据类型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事件。

处理onError事件的拦截器

这个拦截器主要是将异常信息转化为用户”能看懂”的友好提示。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
private class HttpResponseFunc<T> implements Func1<Throwable, Observable<T>> {        @Override        public Observable<T> call(Throwable throwable) {            //ExceptionEngine为处理异常的驱动器            return Observable.error(ExceptionEngine.handleException(throwable));        }    }

两个拦截器以前使用,代码如下:

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public Observable<Weather> getWeather(String cityName){        return weatherService.getWeather(cityName)                //拦截服务器返回的错误                .map(new ServerResponseFunc<Weather>())                //HttpResponseFunc()为拦截onError事件的拦截器                .onErrorResumeNext(new HttpResponseFunc<Weather>());    }

调用:

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
APIWrapper.getInstance().getWeather("北京")                        .subscribe(new SampleProgressObserver<Weather>(MainActivity.this) {                            @Override                            public void onNext(WeatherBean weatherBean) {                                tv.setText(weatherBean.toString());                            }                        });

相关类:

   
   
  • 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
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 Observer<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);}

处理异常的驱动器

   
   
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
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;}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
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

   
   
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
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();        }    }}

可能本博客也不是最好的解决方案,如果有更好的想法,我愿与你互相交流!

代码参考:https://github.com/122627018/Retorfit_RxJava_Exception


分享: Retrofit+RxJava错误预处理

看到bobo_wang的文章,不仅感觉有受益匪浅,这里做下介绍。

首先定义如下Transformer转换器。

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
public static <T> Observable.Transformer<Response<T>, T> sTransformer() {   return responseObservable -> responseObservable.map(tResponse -> {     if (!tResponse.success) throw new RuntimeException(tResponse.code);     return tResponse.data;   }).onErrorResumeNext(new HttpResponseFunc<>()); } public static <T> Observable.Transformer<T, T> switchSchedulers() {    return observable -> observable.subscribeOn(Schedulers.io())        .observeOn(AndroidSchedulers.mainThread());  } private static class HttpResponseFunc<T> implements Func1<Throwable, Observable<T>> {   @Override public Observable<T> call(Throwable throwable) {     //ExceptionEngine为处理异常的驱动器     return Observable.error(new Throwable(throwable));   } }

调用:

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
public void login(View v){  apiservice.login(name,pwd)      .compose(Transformers.sTransformer())      .compose(Transformers.switchSchedulers())      .subscribe(subscriber);}private Subscriber<UserModel> subscriber = new Subscriber<UserModel>() {    @Override public void onCompleted() {      // do onCompleted    }    @Override public void onError(Throwable e) {      // do on success != true;      // do on http error      // do on other error    }    @Override public void onNext(UserModel model) {      // parse data    }  };

接口:

   
   
  • 1
  • 2
@FormUrlEncoded @POST("interface?login")Observable<Response<UserModel>> login(@Field("name") String name,@Field("pwd") String pwd);

最后再来点干货。

Transformer 和 Func 处理的区别

如上的处理,定义了 一个 sTransformer 和一个 HttpResponseFunc, 
从中可以明显感觉的到sTransformer其实也是可以用Func1来定义的,

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
public void login(View v){  apiservice.login(name,pwd)      .compose(Transformers.switchSchedulers())      .map(new TransFuc<UserModel>())      .onErrorReturn(new HttpResponseFunc<>())      .subscribe(subscriber);}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
public static class TransFuc<T> implements Func1<Response<T>, T> {    @Override public T call(Response<T> tResponse) {      if (!tResponse.success) throw new RuntimeException(tResponse.code);      return tResponse.data;    }  }

Transformer作用于整个流,Func1是一个操作符,作用于数据项。

不规范数据的处理

有时候服务器返回的数据并不是十分规范的,比如 
正常返回是这样的

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
{    "success": true, // 是否成功    "status": "1",   // 状态码    "data": {        // 内容    }      }

错误时时这样的

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
{    "success": false, // 是否成功    "status": "0",   // 状态码    "data": "371"   //错误码  }

这时候如果我么用泛型处理

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
public class Response<T> {    public boolean success;    public String status;    public T data;}

针对这种数据,我们的泛型该怎么写成了问题,错误的时候是String,正确的时候是Bean?

如果我们直接写成JavaBean,那么我们会得到一个错误,LinkedTreeMap cannot be cast to xxx 
类型转换错误.这时候只能将泛型写成String来处理了,使用如下Transformer

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
public static Observable.Transformer<String, UserModel> trans() {    return stringObservable -> stringObservable.map(s -> {      Response parseJson = GsonUtil.parseJson(s, Response.class);      if (null == parseJson) {        throw new RuntimeException("null == parseJson");      }      if (PatternsUtil.isNum(parseJson.data.toString())) {        throw new RuntimeException(parseJson.data.toString());      }      return GsonUtil.parseJson(s, UserModel.class);    }).onErrorResumeNext(new HttpResponseFunc<>());  }

使用就变成了如下这样

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
public void login(View v){  apiservice.login(name,pwd)      .compose(Transformers.switchSchedulers())       .compose(Transformers.trans())      .subscribe(subscriber);}

封装的越来越简介了,用到实际项目吧!

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述