RxJava + Retrofit + OKHttp + RxLifecycle进一步封装网络

一、说明

         RxJava和Retrofit的基本用法这里不再阐述,网络上有很多教程,这里只写进一步封装的过程,回顾一下我学习网络封装的知识。


二、封装过程

        1、先把接口的数据格式理清楚,一般返回的Json格式都是这样:

             

{
  "code":200
  "msg":"成功"
  "results":{[data]
            }
}
 


                results里面的才是我们真正需要的实例,但是每个实例都不一样,因此要针对不同的数据来封装。

              因此,先创建一个公共接口实例类HttpResult类,我们真正需要的实例先不初始化,用T来代替(例如MovieBean、GoodsBean这样的),我这里数据结构不一样,因此可能和你的不一样:

              

public class HttpResult<T> {

    private int ret;
    private String msg;
    private String token;

    private T result;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public int getRet() {
        return ret;
    }

    public void setRet(int ret) {
        this.ret = ret;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getResult() {
        return result;
    }

    public void setResult(T result) {
        this.result = result;
    }
}


        2、首先创建一个Retrofit需要的interface,用来获取数据接口

             例如:这里有一个仿拉手团购的公开接口,只有两个数据,这样的:

                         获取团购信息:http://7xij5m.com1.z0.glb.clouddn.com/spRecommend.txt

                         获取电影信息:http://7xij5m.com1.z0.glb.clouddn.com/filmHot_refresh.txt

              因此我的APIService是这样写的:

               

public interface APIService {

    String base_url = "http://7xij5m.com1.z0.glb.clouddn.com/";

    @GET("filmHot_refresh.txt")
    Observable<HttpResult<List<MovieBean>>> getMovie();

    @GET("spRecommend.txt")
    Observable<HttpResult<GoodsBean>> getGoods();

}



             HttpResult里面一个装着电影信息List<MovieBean>,一个装着商品信息GoodsBean。


        3、接口弄好了之后,接下来要初始化我们的Retrofit和OKHttp了,用一个HttpRetrofit来封装:

         

public class HttpRetrofit {
    private static final int DEFAULT_TIMEOUT = 10;

    private final APIService apiService;

    HttpRetrofit(){

        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        builder.readTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
        builder.retryOnConnectionFailure(true);    

        Cache cache = new Cache(App.getAppContext().getCacheDir(),10 * 1024 * 1024);
        builder.cache(cache);
        builder.addInterceptor(new CacheInterceptor());
        builder.addNetworkInterceptor(new CacheInterceptor());

        apiService = new Retrofit.Builder()
                .client(builder.build())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(APIService.base_url)
                .build().create(APIService.class);
    }

    APIService getApiService(){
        return apiService;
    }
}



             如果你会Retrofit的基本使用,上面的代码应该能看懂,CacheInterceptor我就不贴代码了,就是针对网络状况不同是否用缓存的一个判断。


      3、将APIService单例化,用一个HttpFactory类来给外界调用:

            

public class HttpFactory {

    private static APIService apiService;

    private HttpFactory(){

    }

    public static APIService getApiService(){
        if(apiService == null){
            synchronized (HttpFactory.class){
                if(apiService == null){
                    apiService = new HttpRetrofit().getApiService();
                }
            }
        }
        return apiService;
    }
}


     4、现在,我们已经进行了一个基本的封装,但是每次我们调用的时候都要重复写指定线程的代码,这样很麻烦,能不能一次性把它们封装好,以后调用的时候不用再写这一句代码了呢?答案是当然可以,RxJava为我们提供了一个Transformer类,它的作用是用来将一个Observable转换成另一个Observable,调用的时候使用compose操作符就行,它类似于flapmap操作符,但是效率更高(原因好像是它复用了Observable,这个具体我也不太清楚)。我们写一个HttpResultTransformer类:

          

public class HttpTransformer<R extends HttpResult<T>, T>  implements Observable.Transformer<R,T>{

    private ActivityLifecycleProvider provider;

    public HttpTransformer(ActivityLifecycleProvider provider){
        this.provider = provider;
    }

    @Override
    public Observable<T> call(Observable<R> rObservable) {

        return rObservable.flatMap(new Func1<R, Observable<T>>() {
            @Override
            public Observable<T> call(R r) {

                if(r.getRet() != 0){
                    return createData(r.getResult());
                }else {
                    return Observable.error(new ApiException("网络出错"));
                }

            }
        }).subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(provider.<T>bindUntilEvent(ActivityEvent.PAUSE));
    }

    private Observable<T> createData(final T data){
        return Observable.create(new Observable.OnSubscribe<T>() {
            @Override
            public void call(Subscriber<? super T> subscriber) {
                try {
                    subscriber.onNext(data);
                    subscriber.onCompleted();
                }catch (Exception e){
                    subscriber.onError(e);
                }
            }
        });
    }

}
 


               这段代码有点复杂,我来说明一下,首先对我们获取的HttpResult进行一个判断返回码,如果返回码不正确,直接交给Observable去调用Error方法(其中APIException是我自己创建的一个异常类),就不再继续转换和发送请求了;如果返回码正确,那么我们要把HttpResult转换成我们真正需要的result,然后同时指定线程和RxLifecycle的接触绑定的方法,这样,就省略了很多代码。

               在没有封装之前,我们写的代码是这样:

               

private void getGoods(){
        HttpFactory.getApiService()
                .getGoods()
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(this.<HttpResult<GoodsBean>>bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe(new Subscriber<HttpResult<GoodsBean>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(HttpResult<GoodsBean> httpResult) {
                GoodsBean bean = httpResult.getResult();
                    }
                });
    }


             封装之后,代码是这样的:

    private void getGoods2(){
        HttpFactory.getApiService()
                .getGoods()
                .compose(new HttpTransformer<HttpResult<GoodsBean>,GoodsBean>(this))
                .subscribe(new Subscriber<GoodsBean>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(GoodsBean goodsBean) {

                    }
                });
    }


              是不是清爽了很多?APIException的代码,我这里还没有写错误码处理:

public class ApiException extends IllegalAccessException {

    public ApiException(String s) {
        super(s);
    }

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

    private static String getApiExceptionMessage(int code){
        switch (code){
            default:
        }
        return null;
    }


}


          5、最后,在我们访问网络的时候需要弹一个提示框,在访问结束后让提示框消失。

                有一个问题要注意,我们如果在Subscriber的onStart里面弹框的话可能会有线程问题,但是RxJava已经为我们提供了解决办法,subscribeOn方法只有在第一次生效,而第二次subscriberOn时候可以让onStart方法在主线程执行(这里有疑问,应该是让doOnSubscriber在主线程执行,但是我在实际中发现onStart也在主线程执行了)。

               因此,我们可以在Subscriber的onStart中弹框,在onComplete方法中取消弹框。这里,首先用一个ProgressDialogHander来封装我们的ProgressDialog,               
ProgressDialogHander继承Handler,在需要的时候弹框,不需要的时候不弹框(这里有一个 ProgressDialogCannelListener用来做用户主动取消弹框后的操作(一般是取消订阅):

           

public class ProgressDialogHandler extends Handler {

    public static final int SHOW_PROGRESSDIALOG = 1;
    public static final int DISMISS_PROGRESSDIALOG = 2;

    interface OnProgressDialogCannelListener{
        void onCannelProgress();
    }

    private ProgressDialog mDialog;
    private Context mContext;
    private OnProgressDialogCannelListener mListener;
    private boolean canCannel;

    public ProgressDialogHandler(Context context,OnProgressDialogCannelListener listener,boolean canCannel){
        this.mContext = context;
        this.mListener = listener;
        this.canCannel = canCannel;
    }

    private void initProgressDialog(){
        if(mDialog == null){
            mDialog = new ProgressDialog(mContext);
            mDialog.setCanceledOnTouchOutside(false);
            mDialog.setMessage("加载中 ...");
            mDialog.setCancelable(canCannel);
            if(canCannel){
                mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        mListener.onCannelProgress();
                    }
                });
            }
        }
        if(!mDialog.isShowing()){
            mDialog.show();
        }
    }

    private void dissmissDialog(){
        if(mDialog != null){
            mDialog.dismiss();
            mDialog = null;
        }
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case SHOW_PROGRESSDIALOG:
                initProgressDialog();
                break;
            case DISMISS_PROGRESSDIALOG:
                dissmissDialog();
                break;
        }
    }
}

                再写一个类ProgressSubscriber继承Subscriber,用来给用户回调请求成功或者失败的方法:

public abstract class ProgressSubscriber<T> extends Subscriber<T> implements ProgressDialogHandler.OnProgressDialogCannelListener {

    ProgressDialogHandler mHandler;

    public ProgressSubscriber(Context context){
       mHandler = new ProgressDialogHandler(context,this,true);
    }

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



    @Override
    public void onCompleted() {
        dismissDialog();
    }

    @Override
    public void onError(Throwable e) {
        if(false){//TODO 网络不可用
            onFailed("网络不可用!");
        } else if(e instanceof ApiException){
            onFailed(e.getMessage());
        } else {
            onFailed("请求失败,请稍后再试");
        }
    }

    @Override
    public void onNext(T t) {
        onSuccess(t);
    }


    private void showDialog(){
        if(mHandler != null){
            mHandler.sendEmptyMessage(ProgressDialogHandler.SHOW_PROGRESSDIALOG);
        }
    }

    private void dismissDialog(){
        if(mHandler != null){
            mHandler.sendEmptyMessage(ProgressDialogHandler.DISMISS_PROGRESSDIALOG);
            mHandler = null;
        }
    }

    protected abstract void onSuccess(T t);

    protected abstract void onFailed(String message);

    @Override
    public void onCannelProgress() {
        if(!this.isUnsubscribed()){
            this.unsubscribe();
        }
    }
}


                   至此,这次封装就结束了,我们去请求GoodBeans的操作就是这样了:

    private void getGoods2(){
        HttpFactory.getApiService()
                .getGoods()
                .compose(new HttpTransformer<HttpResult<GoodsBean>,GoodsBean>(this))
                .subscribe(new ProgressSubscriber<GoodsBean>(this) {
                    @Override
                    protected void onSuccess(GoodsBean goodsBean) {

                    }

                    @Override
                    protected void onFailed(String message) {

                    }
                });
    }



三、参考博客:

       扔物线大神的RxJava经典教程

       本文大部分学习的封装方法

               

猜你喜欢

转载自blog.csdn.net/vicwudi/article/details/77604346