美文网首页Android进阶Android 入门进阶RxJAva OKHttp Retrofit
搭建自己的框架之1:Rxjava2+Retrofit2 实现An

搭建自己的框架之1:Rxjava2+Retrofit2 实现An

作者: 曾经的你呀 | 来源:发表于2017-04-23 17:17 被阅读8641次
    vanke service

    Demo On GitHub(MVP-Rxjava2-Retrofit2)

    前言

    本文不是单独讲Retrofit2(我上一文章关于Retrofit2),至于http 的知识可以去看《图解HTTP》这本简洁的书,主题是:Rxjava2+Retrofit2 实现Android http 请求。

    封装好后Android 中一个Http请求如下:(onFailed 大部分时候不用重写,在baseobserver中一般都是toast 提示错误。但是需要重新定义网络请求失败需要override)

            HttpCall.getApiService().goLoginByRxjavaObserver(loginParams)
                    .compose(RxObservableUtils.applySchedulers())
                    .compose(bindToLifecycle())    //两个compose 合起来就更简洁了
                    .subscribe(new BaseObserver<LoginResult>() {
                        @Override
                        public void onSuccess(LoginResult loginResult) {
                            loginSuccess(loginResult);
                        }
                        //大部分不用override
                        @Override
                        public void onFailure(int code, String message) {
                            super.onFailure(code, message);
                        }
                    });
    

    其中 .subscribe(new BaseObserver<LoginResult>() 可以知道只是我们需要的http 返回的数据, 范型<T>和我们的Api结构有关,

    http Api 返回结果处理

    我们项目的api 返回的结构大致如下,大同小异:

    api result json

    这种结构在我接触的项目中是非常的常见,整个结构中只有result 是不同的(json obgj/array),使用泛型<T>来处理就行.

    /**
     * 这个类和具体的业务api 结构有关,本Demo的API 结构大致如下:
     * 
     * Created by anylife.zlb@gmail.com on 2016/7/11.
     */
    public class HttpResponse<T> {
        private int code;
        private String error;
        private T result;
            // some set and some get
        public T getResult() {
            return result;
        }
    
        public void setResult(T result) {
            this.result = result;
        }
    }
    

    API 要怎样定义呢?

    根据Retrofit的public interface Call<T> extends Cloneable {...}定义,我们知道要定义的Api Service每个http method中retrofit返回的是Call<HttpResponse<T>>, 和rxjava2结合起来返回 基本类型Observable<HttpResponse<T>> 就行了,自己看看Retrofit 的源代码就知道了。
    泛型 T和具体的Api 有关,重点放在下面和Rxjava2 的结合:

    public interface ApiService {
        /**
         * 获取信息,返回Observable 可以知道是和Rxjava 结合起来用的
         */
        @GET("api/lebang/staffs/me/detail")
        Observable<HttpResponse<StaffMsg>> getStaffMsg();
    
        /**
         * 纯的Retrofit 啊
         */
        @POST("api/lebang/oauth/access_token1")
        Call<HttpResponse<LoginResult>> goLoginByRetrofit(@Body LoginParams loginParams);
    

    结合rxjava2 要怎么写http 请求呢?

    我们知道Rxjava 中最简单的流处理是这样的:Observable.subscribe(Observer);
    这种流式处理很好理解就是http 请求这件事被订阅观察了。

    我们的重点就是封装处理一下 Observable 和 Observer
    以获取员工信息为例子就是

     ApiService.getStaffMsg()     //1.这是observable
                      .subscribeOn(Schedulers.io()) //2.请求在IO线程
                      .observeOn(AndroidSchedulers.mainThread());  //3观察处理数据在主线程
                      .subscribe(new Observer<HttpResponse<StaffMsg>>() { // 4.不解释
                        @Override
                        public void onSubscribe(Disposable d) {      
                        }
                        @Override
                        public void onNext(HttpResponse<StaffMsg> response) {
                           
                        }
                        @Override
                        public void onError(Throwable e) {
    
                        }
    

    你要这样子写也是没有关系的,但是很明显有很多的冗余代码:

    • HttpResponse<T> 返回的数据我们只想要关心T,HttpResponse 其他部分是一样的
    • 没有必要每个接口处理onError ,大部分的Http 的错误异常处理是可以抽取出共性的
    • 订阅的线程和观察的线程都是一样的,也是重复的代码
    • 请求的时候需要一个网络请求的ProgressDialog 的提示怎么办?

    综合上面,我们要再封装 Observer

    先看看Rxjava2 中怎样定义的Observer

    public interface Observer<T> {
        void onSubscribe(Disposable d);
    
        void onNext(T t);
    
        void onError(Throwable e);
    
        void onComplete();
    }
    

    那么我们结合 HttpRespose<T> 就知道怎么封装我们的BaseObserver 了 (仅供参考)

    public abstract class BaseObserver<T> implements Observer<HttpResponse<T>> {
        private final String TAG = BaseObserver.class.getSimpleName();
        private final int RESPONSE_CODE_OK = 0;       //自定义的业务逻辑,成功返回积极数据
        private final int RESPONSE_CODE_FAILED = -1;  //返回数据失败,严重的错误
        private Context mContext;
        private static Gson gson = new Gson();
        private int errorCode;
        private String errorMsg = "未知的错误!";
    
        /**
         * 根据具体的Api 业务逻辑去重写 onSuccess 方法!Error 是选择重写,but 必须Super !
         * @param t
         */
        public abstract void onSuccess(T t);
    
        /**
         * @param mContext
         * @param showProgress 默认需要显示进程,不要的话请传 false
         */
        public  BaseObserver(Context mContext, boolean showProgress) {
            this.mContext = mContext;
            if (showProgress) {
                HttpUiTips.showDialog(mContext, true, null);
            }
        }
        @Override
        public final void onSubscribe(Disposable d) {
            //不管取消,和生命周期绑定
        }
    
        @Override
        public final void onNext(HttpResponse<T> response) {
            HttpUiTips.dismissDialog(mContext);
            //根据业务来分
            if (response.getCode() == RESPONSE_CODE_OK) {
                onSuccess(response.getResult());
            } else {
                onFailure(response.getCode(), response.getError());
            }
        }
    
        @Override
        public final void onError(Throwable t) {
            HttpUiTips.dismissDialog(mContext);
            if (t instanceof HttpException) {
                HttpException httpException = (HttpException) t;
                errorCode = httpException.code();
                errorMsg = httpException.getMessage();
                getErrorMsg(httpException);
            } else if SocketTimeoutException) {  //VPN open
                errorCode = RESPONSE_CODE_FAILED;
                errorMsg = "服务器响应超时";
            } 
            // .....其他的异常处理
            onFailure(errorCode, errorMsg);
        }
    
        /**
         * 简单的把Dialog 处理掉
         */
        @Override
        public final void onComplete() {
        }
    
        /**
         * Default error dispose!
         * 一般的就是 AlertDialog 或 SnackBar
         *
         * @param code
         * @param message
         */
        @CallSuper  //if overwrite,you should let it run.
        public void onFailure(int code, String message) {
            if (code == RESPONSE_CODE_FAILED && mContext != null) {
                HttpUiTips.alertTip(mContext, message, code);
            } else {
                disposeEorCode(message, code);
            }
        }
    
        /**
         * 对通用问题的统一拦截处理
         * @param code
         */
        private final void disposeEorCode(String message, int code) {
            switch (code) {
                case 101:
                case 401:
                    //退回到登录页面
                    Intent intent = new Intent();
                    intent.setClass(mContext, LoginActivity.class);
                    mContext.startActivity(intent);
                    break;
            }
            Toast.makeText(mContext, message + "   code=" + code, Toast.LENGTH_SHORT).show();
        }
    
    
        /**
         * 获取详细的错误的信息 errorCode,errorMsg ,   这里和Api 结构有关,这里和Api 结构有关 
         * 以登录的时候的Grant_type 故意写错为例子,http 应该是直接的返回401=httpException.code()
         * 但是是怎么导致401的?我们的服务器会在respose.errorBody 中的content 中说明
         */
        private final void getErrorMsg(HttpException httpException) {
            String errorBodyStr = "";
            try {   //我们的项目需要的UniCode转码,不是必须要的!
                errorBodyStr = TextUtils.convertUnicode(httpException.response().errorBody().string());
            } catch (IOException ioe) {
                Log.e("errorBodyStr ioe:", ioe.toString());
            }
            try {
                HttpResponse errorResponse = gson.fromJson(errorBodyStr, HttpResponse.class);
                if (null != errorResponse) {
                    errorCode = errorResponse.getCode();
                    errorMsg = errorResponse.getError();
                } else {
                    errorCode = RESPONSE_CODE_FAILED;
                    errorMsg = "ErrorResponse is null";
                }
            } catch (Exception jsonException) {
                errorCode = RESPONSE_CODE_FAILED;
                errorMsg = "http请求错误Json 信息异常";
                jsonException.printStackTrace();
            }
        }
    
    }
    

    这里的失败处理和我们的协议有关,比如: 比如oauth(loginin) http 状态码是401.实际的信息是在response 的 error body里。
    errorCode = httpException.code(); errorMsg = httpException.getMessage();

    这样简单的封装后我们的调用就可以非常的简单了 ,其中onFailure 很多接口都可以不Override,直接的处理onSuccess 返回的数据就行了。
    .compose(RxObservableUtils.applySchedulers()) 就处理线程的 切换的,把

    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());

    变为 一行代码了,在Activity 中的调用如下,大部分情况不Override onFailure会更加的简单:

            HttpCall.getApiService().getStaffMsg()
                    .compose(RxObservableUtils.applySchedulers())
                    .subscribe(new BaseObserver<StaffMsg>(this,true) {
                        @Override
                        public void onSuccess(StaffMsg staffMsg) {
                            
                        }
    
                        @Override
                        public void onFailure(int code, String message) {
                            super.onFailure(code, message);
                        }
                    });
    

    Rxjava 和Retrofit 结合实现Http需要注意

    Demo On GitHub(MVP-Rxjava2-Retrofit2)

    https://github.com/AnyLifeZLB/Retrofit2.0_Demo (针对本文的裁剪版Demo)

    下一篇链接: 搭建自己的框架之2:MVP+Rxjava2

    相关文章

      网友评论

      • 4b01863fd657:支持一下
      • 清平湖水浪打蛋:你好,我导入你的项目以后出现这个错误 不知道怎么解决
        Error:Could not download leakcanary-android.aar (com.squareup.leakcanary:leakcanary-android:1.5): No cached version available for offline mode
        曾经的你呀:@清平湖水浪打蛋 重启一下AS,开VPN下载,你也要看看leakcanary GitHub 现在版本是否是1.5
      • Hancock1993:刚看完RxJava,正要写代码练练手,导包的时候发现不对劲,一搜才发现,都2.0了!
        哎,gtmd....
        曾经的你呀::smile:,欢迎交流指导
      • _SOLID:使用 rxjava2 + reatrofit2 做网络请求的时候我认为使用Single比Observable更好,毕竟网络请求响应只会有一次,不会多次发送。
        A_si:singled的话,就不能取消订阅了。
        准备流浪的雪:@iSnowFlake 不是应该是disposable么,还能去取消
        曾经的你呀:@_SOLID 按照官方说明是这样子的,我没有去实验实际效果的差异。感谢指教

      本文标题:搭建自己的框架之1:Rxjava2+Retrofit2 实现An

      本文链接:https://www.haomeiwen.com/subject/eqeyzttx.html