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

来源:互联网 发布:unity3d树叶是方形的 编辑:程序博客网 时间:2024/06/09 15:57

一、说明

         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经典教程

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

               
阅读全文
0 0