Rxjava+ReTrofit+okHttp深入浅出-终极封装

标签: rxjava retrofit okhttp | 发表时间:2016-07-18 19:47 | 作者:u014610664
出处:http://blog.csdn.net

Rxjava+ReTrofit+okHttp深入浅出-终极封装
背景:
学习Rxjava和retrofit已经很长时间了,功能确实很强大,但是使用起来还是有点复杂,代码的重复性太高,所以决定把基于retrofit和rxjava的处理统一封装起来,实现的功能:
        1.Retrofit+Rxjava+okhttp基本使用方法
    2.统一处理请求数据格式
    3.统一的ProgressDialog和回调Subscriber处理
    4.取消http请求
    5.预处理http请求
    5.返回数据的统一判断

效果:
封装后http请求代码如下
 //    完美封装简化版
    private void simpleDo() {
        SubjectPost postEntity = new SubjectPost(new ProgressSubscriber(simpleOnNextListener, this), true);
        HttpManager manager = HttpManager.getInstance();
        manager.doHttpDeal(postEntity);
    }

    //   回调一一对应
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
        @Override
        public void onNext(List<Subject> subjects) {
            tvMsg.setText("已封装:\n" + subjects.toString());
        }
    };>
是不是很简单?你可能说这还简单,好咱们对比一下正常使用retrofit的方法
 /**
     * Retrofit加入rxjava实现http请求
     */
    private void onButton9Click() {
        //手动创建一个OkHttpClient并设置超时时间
        okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(5, TimeUnit.SECONDS);

        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(HttpManager.BASE_URL)
                .build();

//        加载框
        final ProgressDialog pd = new ProgressDialog(this);

        HttpService apiService = retrofit.create(HttpService.class);
        Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);
        observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        new Subscriber<RetrofitEntity>() {
                            @Override
                            public void onCompleted() {
                                if (pd != null && pd.isShowing()) {
                                    pd.dismiss();
                                }
                            }

                            @Override
                            public void onError(Throwable e) {
                                if (pd != null && pd.isShowing()) {
                                    pd.dismiss();
                                }
                            }

                            @Override
                            public void onNext(RetrofitEntity retrofitEntity) {
                                tvMsg.setText("无封装:\n" + retrofitEntity.getData().toString());
                            }

                            @Override
                            public void onStart() {
                                super.onStart();
                                pd.show();
                            }
                        }

                );
    }
可能你发现确是代码有点多,但是更加可怕的是,如果你一个activity或者fragment中多次需要http请求,你需要多次重复的写回调处理(一个回到就有4个方法呀!!!!反正我是忍受不了),而且以上处理还没有做过多的判断和错误校验就如此复杂!~好了介绍完了,开始咱们的优化之路吧!

项目结构:


Rxjava
如果你对Rxjava不了解,好吧骚年赶快学学吧,不然真会out了******* 抛物线大大的金典rxjava
进阶:
ReTrofit基本设置
咱家今天的主角来了,咱们也深入浅出一下,前方高能,如果你是深度retrofit选手请直接跳过本节!!!
1.首先确保在AndroidManifest.xml中请求了网络权限 
  <uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
    /*rx-android-java*/
    compile 'io.reactivex:rxjava:+'
    compile 'com.squareup.retrofit:adapter-rxjava:+'
    compile 'com.trello:rxlifecycle:+'
    compile 'com.trello:rxlifecycle-components:+'
    /*rotrofit*/
    compile 'com.squareup.retrofit2:retrofit:+'
    compile 'com.squareup.retrofit2:converter-gson:+'
    compile 'com.squareup.retrofit2:adapter-rxjava:+'
    compile 'com.google.code.gson:gson:+'

ReTrofit基本使用:

后面的文章中我们都是用这个接口调试
/**
 * @api    videoLink    50音图视频链接
 * @url    http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
 * @method post
 * @param  once_no bool(选填,ture无链接) 一次性获取下载地址
 * @return json array(
 * ret:1成功,2失败
 * msg:信息
 * data:{
 *       name:视频名称
 *       title:标题
 * }
 )
 



1.初始化retrofit
要向一个api发送我们的网络请求 ,我们需要使用  Retrofit builder 类并指定service的base URL (通常情况下就是域名)。
    String BASE_URL = " http://www.izaodao.com/Api/"
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
2.设置接口service
注意到每个endpoint 都指定了一个关于HTTP(GET, POST, 等等。)  方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。

/**
 * 接口地址
 * Created by WZG on 2016/7/16.
 */
public interface MyApiEndpointInterface {

    @POST("AppFiftyToneGraph/videoLink")
    Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
}
3.得到call然后同步处理处理回调:
        MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
        Call<RetrofitEntity> call = apiService.getAllVedio(true);
        call.enqueue(new Callback<RetrofitEntity>() {
            @Override
            public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
                RetrofitEntity entity = response.body();
                Log.i("tag", "onResponse----->" + entity.getMsg());
            }

            @Override
            public void onFailure(Throwable t) {
                Log.i("tag", "onFailure----->" + t.toString());

            }
        });


ReTrofit+Rxjava基本使用
和之前的retrofit使用区别:
1.在于我们需要修改service接口返回信息我们需要返回一个Observable对象
@POST("AppFiftyToneGraph/videoLink")
    Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit需要添加对Rxjava的适配,注意一定要retrofit2才有这个功能哦
Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(HttpManager.BASE_URL)
                .build();
3.回调交个rxjava去处理
 HttpService apiService = retrofit.create(HttpService.class);
        Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);
        observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        new Subscriber<RetrofitEntity>() {
                            @Override
                            public void onCompleted() {
                            }

                            @Override
                            public void onError(Throwable e) {                              
                            }

                            @Override
                            public void onNext(RetrofitEntity retrofitEntity) {
                                tvMsg.setText("无封装:\n" + retrofitEntity.getData().toString());
                            }                          
                        }

                );

ReTrofit+Rxjava进阶封装之路

1.请求操作类封装,统一处理过程:
1.1首先初始化一个单利方便http请求
这里用了volatile的对象, 不懂的同学可以参考我的另一篇博客
private volatile static HttpManager INSTANCE;
    //构造方法私有
    private HttpManager() {
        //手动创建一个OkHttpClient并设置超时时间
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
        httpService = retrofit.create(HttpService.class);
    }

    //获取单例
    public static HttpManager getInstance() {
        if (INSTANCE == null) {
            synchronized (HttpManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new HttpManager();
                }
            }
        }
        return INSTANCE;
    }
1.2接口处理和回调处理:
basePar.getObservable主要是得打service中定义的observable对象(我们需要处理的接口对象)
RxMap方法是统一处理结果回调信息处理
basePar.getSubscirber获取需要统一结果处理的subscirber对象
/**
     * 处理http请求
     *
     * @param basePar 封装的请求数据
     */
    public void doHttpDeal(BaseEntity basePar) {
        Observable observable = basePar.getObservable(httpService)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(basePar);
        observable.subscribe(basePar.getSubscirber());
    }
可以发现主要的封装都放入了BaseEntity中,我们跟着思路进入到BaseEntity

2.数据统一请求参数和数据封装BaseEntity
2.1:getObservable通过传入一个**service对象获取需要处理的Observable对象
2.2:getSubScirber得到一个回调sub对象
这两个方法都需要其子类实现覆盖
2.3:在base里面我们继承Fucn1方法通过泛型去处理服务器返回数据判断(这时候我们可以在回调信息前提前处理错误信息),可以自己封装信息哦!如果是有效数据才返回给next方法,错误信息或者无数据等直接抛出一个自己定义的异常信息在onerror方法中处理!
/**
 * 请求数据统一封装类
 * Created by WZG on 2016/7/16.
 */
public abstract class BaseEntity<T> implements Func1<BaseResultEntity<T>, T> {
    /**
     * 设置参数
     *
     * @param methods
     * @return
     */
    public abstract Observable getObservable(HttpService methods);

    /**
     * 设置回调sub
     *
     * @return
     */
    public abstract Subscriber getSubscirber();
    @Override
    public T call(BaseResultEntity<T> httpResult) {
        if (httpResult.getRet() == 0) {
            throw new HttpTimeException(httpResult.getMsg());
        }
        return httpResult.getData();
    }
}

3自定义异常处理
/**
 * 自定义错误信息,统一处理返回处理
 * Created by WZG on 2016/7/16.
 */
public class HttpTimeException extends RuntimeException {

    public static final int NO_DATA = 0x2;

    public HttpTimeException(int resultCode) {
        this(getApiExceptionMessage(resultCode));
    }

    public HttpTimeException(String detailMessage) {
        super(detailMessage);
    }

    /**
     * 转换错误数据
     *
     * @param code
     * @return
     */
    private static String getApiExceptionMessage(int code) {
        String message = "";
        switch (code) {
            case NO_DATA:
                message = "无数据";
                break;
            default:
                message = "error";
                break;

        }
        return message;
    }
}

4.BaseEntity集成覆盖主要方法,实现处理的和返回的一一绑定
/**
 * 测试接口请求数据
 * Created by WZG on 2016/7/16.
 */
public class SubjectPost extends BaseEntity {
    //    回调sub
    private Subscriber mSubscriber;
    private boolean all;


    public SubjectPost(Subscriber getTopMovieOnNext, boolean all) {
        this.mSubscriber = getTopMovieOnNext;
        this.all = all;
    }

    @Override
    public Observable getObservable(HttpService methods) {
        return methods.getAllVedioBys(all);
    }

    @Override
    public Subscriber getSubscirber() {
        return mSubscriber;
    }

}

5.然后定义service接口按照泛型的方式传递返回接口的数据
  @POST("AppFiftyToneGraph/videoLink")
    Observable<BaseResultEntity<List<Subject>>> getAllVedioBys(@Body boolean once_no);

6.定义返回数据的基础类,统一处理结果信息:

/**
 * 回调信息统一封装类
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity<T> {
    //  判断标示
    private int ret;
    //    提示信息
    private String msg;
    //显示数据(用户需要关心的数据)
    private T data;}


 7.封装回调的subservice统一处理加载框,错误提示,结果回调等处理

我们在subserice的四个回调方法中,统一处理onstart中我们现实加载框,onerror中统一处理系统错误和自定义错误信息,我们可以在baseentity的 funct1方法中统一处理返回信息,比如咱们测试方法我们可以统一判断ret参数,如果返回的是2我们抛出一个自定义的错误;或者返回的array数据为空我们也可以统一抛出一个空数据的异常!在oonext方法中我们返回一个泛型接口,传出最后service接口指定的泛型的对象!
/**
 * 用于在Http请求开始时,自动显示一个ProgressDialog
 * 在Http请求结束是,关闭ProgressDialog
 * 调用者自己对请求数据进行处理
 * Created by WZG on 2016/7/16.
 */
public class ProgressSubscriber<T> extends Subscriber<T> {
    //    回调接口
    private HttpOnNextListener mSubscriberOnNextListener;
    //    弱引用反正内存泄露
    private WeakReference<Context> mActivity;
    //    是否能取消请求
    private boolean cancel;
    //    加载框可自己定义
    private ProgressDialog pd;

    public ProgressSubscriber(HttpOnNextListener mSubscriberOnNextListener, Context context) {
        this.mSubscriberOnNextListener = mSubscriberOnNextListener;
        this.mActivity = new WeakReference<>(context);
        this.cancel = false;
        initProgressDialog();
    }

    public ProgressSubscriber(HttpOnNextListener mSubscriberOnNextListener, Context context, boolean cancel) {
        this.mSubscriberOnNextListener = mSubscriberOnNextListener;
        this.mActivity = new WeakReference<>(context);
        this.cancel = cancel;
        initProgressDialog();
    }


    /**
     * 初始化加载框
     */
    private void initProgressDialog() {
        Context context = mActivity.get();
        if (pd == null && context != null) {
            pd = new ProgressDialog(context);
            pd.setCancelable(cancel);
            if (cancel) {
                pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        onCancelProgress();
                    }
                });
            }
        }
    }


    /**
     * 显示加载框
     */
    private void showProgressDialog() {
        Context context = mActivity.get();
        if (pd == null || context == null) return;
        if (!pd.isShowing()) {
            pd.show();
        }
    }


    /**
     * 隐藏
     */
    private void dismissProgressDialog() {
        if (pd != null && pd.isShowing()) {
            pd.dismiss();
        }
    }


    /**
     * 订阅开始时调用
     * 显示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
    }

    /**
     * 完成,隐藏ProgressDialog
     */
    @Override
    public void onCompleted() {
        dismissProgressDialog();
    }

    /**
     * 对错误进行统一处理
     * 隐藏ProgressDialog
     *
     * @param e
     */
    @Override
    public void onError(Throwable e) {
        Context context = mActivity.get();
        if (context == null) return;
        if (e instanceof SocketTimeoutException) {
            Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ConnectException) {
            Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "错误" + e.getMessage(), Toast.LENGTH_SHORT).show();
            Log.i("tag", "error----------->" + e.toString());
        }
        dismissProgressDialog();
    }

    /**
     * 将onNext方法中的返回结果交给Activity或Fragment自己处理
     *
     * @param t 创建Subscriber时的泛型类型
     */
    @Override
    public void onNext(T t) {
        if (mSubscriberOnNextListener != null) {
            mSubscriberOnNextListener.onNext(t);
        }
    }

    /**
     * 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求
     */
    public void onCancelProgress() {
        if (!this.isUnsubscribed()) {
            this.unsubscribe();
        }
    }
}

8.自定义返回接口,泛型传递返回值
/**
 * 成功回调处理
 * Created by WZG on 2016/7/16.
 */
public interface HttpOnNextListener<T> {
    void onNext(T t);
}

9.发起http请求处理

定义一个指定类型的回调类httpOnextListener,然后初始化一个统一处理返回结果的ProgressSubscriber对象,将sub传入给封装的BaseEntity对象中,发起网络请求,通过Rxjava后台处理网络请求,主线程处理返回结果;网络请求onext前,调用BaseEntity的fun1方法,统一判断返回信息(可预处理错误),最后通过自定义泛型httpOnextListener返回成功数据
   //    完美封装简化版
    private void simpleDo() {
        SubjectPost postEntity = new SubjectPost(new ProgressSubscriber(simpleOnNextListener, this), true);
        HttpManager manager = HttpManager.getInstance();
        manager.doHttpDeal(postEntity);
    }

    //   回调一一对应
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
        @Override
        public void onNext(List<Subject> subjects) {
            tvMsg.setText("已封装:\n" + subjects.toString());
        }
    };

啰嗦完了,如果多大家有帮助,欢迎留言哦!源代码地址


作者:u014610664 发表于2016/7/18 11:47:15 原文链接
阅读:95 评论:0 查看评论

相关 [rxjava retrofit okhttp] 推荐:

Rxjava+ReTrofit+okHttp深入浅出-终极封装

- - CSDN博客推荐文章
Rxjava+ReTrofit+okHttp深入浅出-终极封装. 学习Rxjava和retrofit已经很长时间了,功能确实很强大,但是使用起来还是有点复杂,代码的重复性太高,所以决定把基于retrofit和rxjava的处理统一封装起来,实现的功能:. 1.Retrofit+Rxjava+okhttp基本使用方法.

Retrofit全攻略——进阶篇

- - CSDN博客推荐文章
最近事比较多,距离上次写文章已经过去了一个月了. 上一篇文章 Retrofit全攻略——基础篇 介绍了Retrofit的基础用法,这篇文章介绍点进阶的用法. 在开发阶段,为了方便调试,我们需要查看网络日志. 因为 Retrofit2.0+底层是采用的 OKHttp请求的. 可以给OKHttp设置拦截器,用来打印日志.

OkHttp,一次无奈的使用

- - 掘金后端
一次使用OKHTTP的心痛历程. 最近由于一些不得已的原因,接触到了OKHttp,说起来也挺Dan疼的,之前同事将生产附件上传地址配置成了测试地址,还好数量不多,没有造成太大的影响,况且的是这位同事又离职了,当时只能在心中 默默的问候了他N遍,当然问候完了之后,也不得不继续数据同步的工作. OKHTTP官方地址: okHttp.

Android Https相关完全解析 当OkHttp遇到Https

- - CSDN博客推荐文章
本文出自: 【张鸿洋的博客】. 其实这篇文章理论上不限于okhttp去访问自签名的网站,不过接上篇博文了,就叫这个了. 首先要了解的事,okhttp默认情况下是支持https协议的网站的,比如 https://www.baidu.com, https://github.com/hongyangAndroid/okhttp-utils等,你可以直接通过okhttp请求试试.

浅谈我为什么选择用Retrofit作为我的网络请求框架

- - CSDN博客推荐文章
比较AsyncTask、Volley、Retrofit三者的请求时间. Retrofit2.0 完胜. 地址: http://ip.taobao.com/service/getIpInfo.php. //@Query注解的作用理解为查询条件,这里表示需要查询的字段为ip. //ResponseBody是Retrofit自带的返回类,.

这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

- - CSDN博客移动开发推荐文章
在 Andrroid开发中,网络请求十分常用. 而在 Android网络请求库中, Retrofit是当下最热的一个网络请求库. 今天,我将献上一份非常详细 Retrofit v2.0的使用教程,希望你们会喜欢. 如果对 Retrofit v2.0的源码感兴趣,可看文章: Android:手把手带你深入剖析 Retrofit 2.0 源码.

OkHttp透明压缩,收获性能10倍,外加故障一枚

- - 掘金后端
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. 要使用 OkHttp,一定要知道它的 透明压缩,否则死都不知道怎么死的;或者活也不知道为什么活的不舒坦. OkHttp在发送请求的时候,会自动加入gzip请求头 Accept-Encoding:gzip. 所以,当返回的数据带有gzip响应头时 Content-Encoding=gzip,OkHttp会自动帮我们解压数据.