Retrofit + RxJava + OkHttp 的简单封装

第一次写有点小激动 算是总结吧!

最近一直听群友说Retrofit + Rxjava + OkHttp有多牛逼多好用多强大....让我产生了很大兴趣....然后去网络上搜索了一翻....很多资料....但是对于基础不是很好的我....很不友好....(资质太低不要嘲笑我)....看完基本还是没明白....

首先推荐一篇看过觉得好的文章
给初学者的RxJava2.0教程 (作者Season_zlc)

阅读以下内容 请先把Retrofit + RxJava + OkHttp先了解一下 这样看下面的东西 才能对你理解起来有帮助

OkHttp

OkHttp是时下最火的HTTP请求框架,别问我为什么,Google都放弃了httpclient,用OkHttp来替代他,你还要问么? so....学习一个新的请求框架,适应形势,还是非常有必要的!

Retrofit

Retrofit + OkHttp我之前学习的时候,看别人的代码一直不知道Retrofit和OkHttp二者是有什么区别? 查阅资料后了解到,OkHttp与HTTPClient、HTTPConnection是一个级别的,是系统提供的较为原装的网络请求库。而Retrofit则是基于OkHttp的再次封装过,与Volley这种已经被封装过的网络库是一个级别的。其实就是套了一层把很多的坑都给你门填好了,而且最主要的是,做了很多安全上的改进,还有能更好的支持RxJava。

Rxjava

看完[Season_zlc]的讲解后,让我了解到了RxJava 是通过实现了线程调度器让开发者更方便且自由的调度线程,被观察者和订阅者的方式和强大的操作符,还有引以为傲的链式调用,满足者开发者各种需求。

那为什么要RxJava + Retrofit + OkHttp要一起使用呢?

其实原因很简单Retrofit用过的人都知道,Retrofit 负责请求,OkHttp 负责请求过程,Retrofit 和 OkHttp 2者结合都没有实现结果成功后在UI线程回调,而是在IO线程回调的。那你肯定是不能更新控件,然道你要打算去写handle来通知UI线程更新么,你不觉得很low么? 而且代码逻辑很乱么? 而使用Rxjava不仅让你的代码看起来更加优雅易懂,并且Retrofit本身也是支持RxJava的。个人想法,也许作者最初的目的本来就是为了和RxJava配套使用。

以下代码均取自Fazhi项目wanggang老师的教学代码 最后我会提供代码供大家下载 不用担心!

一、首先我门定义API类

public interface Iapi {
    @POST("userLogin")
    Observable<BaseResponse<UserBean>> userLogin(@Body RequestBody params);
}

二、初始化Retrofit + OkHttp的请求类 (已经按步骤在代码里注释好了)

这个类的代码很明显,我门先将繁琐的Retrofit和OkHttp初始化,然后通过retrofit.create(Iapi.class)拿到最先定义的API类的实例类对象,有了实例类对象后就可以将RequestBody请求体传入发起请求了。

public class NetRequest {

    private static final String TAG = "===NET REQUEST===";
    private static NetRequest instance;
    private OkHttpClient client;
    private Retrofit retrofit;
    private Iapi iapi;

    // 第一步 构造RequestBody请求体
    public static RequestBody generateReqBody(HashMap<String, Object> map){
        JSONObject params = new JSONObject();
        params.putAll(map);
        return RequestBody.create(NetConfig.TypeJSON, params.toJSONString());
    }

    // 第二步 构建单例模式
    public static NetRequest getInstance() {
        if (instance == null) {
            synchronized (NetRequest.class) {
                if (instance == null) {
                    instance = new NetRequest();
                }
            }
        }
        return instance;
    }

    // 第二步 1.获得单例模式时会初始化该类 初始化时就构造好OkHttp和Retrofit2个对象 并且设置好参数
    public NetRequest() {
        client = getClient();
        retrofit = getRetrofit();
    }

    // 第二步 2.初始化OkHttp的参数 并拿到Client请求对象
    private OkHttpClient getClient() {
        // HttpLoggingInterceptor是Log拦截器
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.i(TAG, message);
            }
        });
        // 设置Log拦截器的拦截等级
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        return new OkHttpClient.Builder()
                               .addInterceptor(interceptor)// 拦截器设置给它
                               .connectTimeout(20, TimeUnit.SECONDS)// 请求超时时间
                               .readTimeout(20, TimeUnit.SECONDS)// 读取超时时间
                               .writeTimeout(20, TimeUnit.SECONDS)// 写入超时时间
                               .build();
    }

    // 第二步 3.初始化Retrofit的参数 并将Client设置给它
    private Retrofit getRetrofit() {
        return new Retrofit.Builder()
                           .client(client)// 传入OKhttp的client对象
                           .addConverterFactory(GsonConverterFactory.create())// 添加gson解析的支持
                           .addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 添加支持observable类型
                           .baseUrl(NetConfig.BASE_URL)// 传入URL
                           .build();
    }

    // 第四步 通过retrofit创建Iapi的实现类对象(retrofit已经帮你实现好了 不需要自己去实现 只要调用方法)
    // 待会model要用来调你写好的Iapi里的接口 接口里定义好了方法为userLogin(放第一步获取的请求体)
    public Iapi getApi() {
        if (iapi == null) {
            iapi = retrofit.create(Iapi.class);
        }
        return iapi;
    }
}

三、重写了Subscriber订阅者类

封装了一些每次回调可以不变的代码,并将onNext这个成功的回调丢给后面的继承者
另外:
1.该类是一个抽象类,继承Subscriber但并没有重写onNext()方法,因为后面要留给Presenter的实现类去回调成功后做各种操作,因此它并不属于重复而又不变的代码,所以在该类里并没有去实现它。
2.onError() 中我门做了统一的服务器错误代码处理,将错误代码区分开来,这里分二种情况:

  1. 请求服务器失败了,通常为网络连接失败404 500之类的
  2. 网络通畅请求成功了,但是给你返回了约定好的错误代码,通常1000 1001之类的,比如帐号不存在 未注册等等....
public abstract class NetSubscriber<T> extends Subscriber<T> {

    private BaseView view;

    @Override
    public void onStart() {
        super.onStart();
        view = getView();
        view.showLoading();
    }

    @Override
    public void onCompleted() {
        view.hideLoading();
    }

    @Override
    public void onError(Throwable e) {
        view.hideLoading();

        ExceptionHandle.ResponeThrowable throwable;
        //后台逻辑执行失败,抛出的异常
        if(e instanceof ExceptionHandle.ResponeThrowable){
            throwable = (ExceptionHandle.ResponeThrowable) e;
        } else {
            //网络操作执行的异常
            throwable = ExceptionHandle.handleException(e);
        }
        view.showError(throwable.message);
    }

    public abstract BaseView getView();
}

四、封装RxJava的请求参数

1.Presenter中RxJava每次都要写初始化参数,比如设置请求时在IO线程,请求完后在UI线程。
2.设置拦截器好比Rxjava发了一个消息下来,中间被你拦截起来,按你设定的过滤规则决定让消息走哪边。
例如我门下面代码中,规则是:
如果服务器返回为true,我门放行让订阅者在onNext()接受到成功的json数据
如果服务器返回为false,我门让他走另一条路exception,让他进入统一错误代码处理中,然后回调订阅者的onErrer()提示用户,失败了的原因。

public class RxHelper {

    // 设置请求时为IO线程,处理结果时在UI线程
    public static Observable.Transformer schedulers() {
        return new Observable.Transformer() {
            @Override
            public Object call(Object observable) {
                return ((Observable)observable)
                        .subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

    // 本方法为设置拦截器
    // 1.用了Map操作符 来拦截每一次的消息 过滤正确的放行给订阅者 失败的丢给错误码统一处理类
    // 2.onErrorResumeNext设置如果是请求上的错误 比如网络错误/连接超时之类的 直接丢给错误码统一处理类
    public static <T> Observable.Transformer transform() {
        return new Observable.Transformer() {
            @Override
            public Object call(Object observable) {
                return ((Observable)observable)
                        .map(new TransformerFun())// 设置拦截服务器连接成功后返回的内容 然后筛选
                        .onErrorResumeNext(new ServerExceptionFun());// 设置请求直接失败了的拦截
            }
        };
    }

    /**
     * 请求失败 将错误代码的回调转接给统一处理类
     * @param <T>
     */
    static class ServerExceptionFun<T> implements Func1<Throwable,Observable<T>>{

        @Override
        public Observable<T> call(Throwable throwable) {
            return Observable.error(ExceptionHandle.handleException(throwable));
        }
    }

    /**
     * 拦截器的具体逻辑
     * @param <T>
     */
    static class TransformerFun<T> implements Func1<BaseResponse<T>,T>{

        @Override
        public T call(BaseResponse<T> baseResponse) {
            if(baseResponse.is_success()){
                return baseResponse.getData();// 正确返回json
            }
            // 如果不正确 丢给错误代码统一处理类
            ExceptionHandle.ServerException exception = new ExceptionHandle.ServerException();
            exception.message = baseResponse.getError_content();
            throw exception;
        }
    }
}

五、错误代码统一处理类

public class ExceptionHandle {
    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 ResponeThrowable handleException(Throwable e) {
        ResponeThrowable ex;
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            ex = new ResponeThrowable(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 ResponeThrowable(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
            ex.message = "解析错误";
            return ex;
        } else if (e instanceof ConnectException) {
            ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
            ex.message = "连接失败";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
            ex.message = "证书验证失败";
            return ex;
        } else if (e instanceof ConnectTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "连接超时";
            return ex;
        } else if (e instanceof java.net.SocketTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "连接超时";
            return ex;
        } else {
            ex = new ResponeThrowable(e, ERROR.UNKNOWN);
            ex.message = "未知错误";
            return ex;
        }
    }


    /**
     * 约定异常
     */
    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 static final int SSL_ERROR = 1005;

        /**
         * 连接超时
         */
        public static final int TIMEOUT_ERROR = 1006;
    }

    public static class ResponeThrowable extends Exception {
        public int code;
        public String message;

        public ResponeThrowable(Throwable throwable, int code) {
            super(throwable);
            this.code = code;

        }
    }

    public static class ServerException extends RuntimeException {
        public int code;
        public String message;
    }
}

六、定义我门自己的BaseResponse成功响应的接收类

该类可有可无,主要用来替代Retrofit自带的ResponseBody,因为我门做了过滤,要用到is_success,当然如果你不做拦截也可以用他原来的ResponseBody。

public class BaseResponse<T> {

    private boolean is_success;
    private String error_content;
    private T data;

    public boolean is_success() {
        return is_success;
    }

    public void setIs_success(boolean is_success) {
        this.is_success = is_success;
    }

    public String getError_content() {
        return error_content;
    }

    public void setError_content(String error_content) {
        this.error_content = error_content;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

最后调用一下看看

HashMap<String, Object> map = new HashMap<>();
map.put("username", username);
map.put("password", passwrod);
RequestBody requestBody = NetRequest.generateReqBody(map);
Observable<BaseResponse<UserBean>> observable = NetRequest.getInstance().getApi() .userLogin(requestBody);
observable.compose(RxHelper.schedulers())
          .compose(RxHelper.transform())
          .subscribe(new NetSubscriber<UserBean>() {
               @Override
               public BaseView getView() { return view; }
               @Override
               public void onNext(UserBean userBean) {
                   // 成功的回调
               }
           });

对~老铁....就是这样....复制+粘贴....第一次....就这样没了

续 结合本封装的MVP项目架构 让你的Activity腰围瘦下来

FaZhi项目框架 Git下载 (向作者wanggang老师致敬)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容