Retrofit2+Rxjava2联网的封装

前言

开发过程中,一般都会对网络框架进行再次封装,以配置各种参数,并适合自己的编码风格。各种网络框架比较下来,还是Retrofit2+Rxjava2看着最爽,今天把这个东西整理了一下,发出来,示例给出了一般写法和MVP的写法。

Retrofit2+Rxjava2

Retrofit2Rxjava2基础的东西就不说了,直接进入主题。我们的需求是:

1、尽可能简洁
2、可控制不同请求的加载框,
3、错误统一处理
4、页面销毁时取消订阅
5、可根据不同请求处理不同的异常

先看一下最终的效果:

  Api.getDefaultService()
                .calendarBean("2017-06-29")
                .map(new RxFunction<Calendar>())
                .compose(RxSchedulers.<Calendar>io_main())
                .subscribe(new RxObserver<Calendar>(this, TAG, 0, false) {
                    @Override
                    public void onSuccess(int whichRequest, Calendar calendar) {
                        tv_.setText(calendar.getLunar());
                    }

                    @Override
                    public void onError(int whichRequest, Throwable e) {

                    }
                });

这个例子其中就包含了以上5点要求,其中大多是对RxObserver的处理。RxObserver实现Observer接口,在Rxjava2中作为观察者,在执行onNext之前先做一些处理,就能相应地减少在View层的处理。


    public RxObserver(Context context, String key, int whichRequest, boolean isShowDialog) {
        this.mContext = context;
        this.mKey = key;
        this.isShowDialog = isShowDialog;
        this.mWhichRequest = whichRequest;
        mDialog = new ProgressDialog(context);
        mDialog.setTitle("请稍后");

        mRxManager = RxManager.getInstance();
    }

参数说明:
keykey是用来区分不同类中联网的CompositeDisposable的,以便在这个类销毁时,可以取消该类中订阅关系,建议采用包名+类名作为key,这个后面会详细说。
whichRequest:区分不同的请求,用于多个联网请求结束后对不同请求的处理。
isShowDialog:是否显示加载框,RxObserver内部实例化了一个加载框,可根据需求设置是否显示。

RxObserver实现了Observer的四个方法 void onSubscribe(@NonNull Disposable d);void onNext(@NonNull T t);void onError(@NonNull Throwable e);void onComplete();

    @Override
    public final void onSubscribe(Disposable d) {
        mRxManager.add(mKey, d);
        if (isShowDialog) {
            mDialog.show();
        }
        onStart(mWhichRequest);
    }

onSubscribe(Disposable d)方法,相当于Rxjava1中的onStar()方法,其中的参数是Disposable ,用于取消该订阅关系,所以在方法中把它添加进了RxManager中,以方便取消订阅。同时也判断了isShowDialog了是否显示加载框,添加了一个方法onStart()方法,同时把mWhichRequest传出去方便在外部回调。

 @Override
    public final void onNext(T value) {
        onSuccess(mWhichRequest, value);
    }

onNext(T value)方法比较简单,联网结果成功返回会执行,参数是结果。在这个方法中写了抽象方法onSuccess(mWhichRequest, value);同样把mWhichRequest传出,方便处理。

@Override
    public final void onComplete() {
        if (mDialog.isShowing()) {
            mDialog.dismiss();
        }
    }

onComplete()方法是联网正常返回,联网过程结束时执行,在该方法中判断这个加载框的显示与否。

   @Override
    public final void onError(Throwable e) {
        if (mDialog.isShowing()) {
            mDialog.dismiss();  
        }
        if (e instanceof EOFException || e instanceof ConnectException || e instanceof SocketException || e instanceof BindException || e instanceof SocketTimeoutException || e instanceof UnknownHostException) {
            Toast.makeText(mContext, "网络异常,请稍后重试!", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ApiException) {
            onError(mWhichRequest, e);
        } else {
            Toast.makeText(mContext, "未知错误!", Toast.LENGTH_SHORT).show();
        }
    }

onError(Throwable e)方法稍微复杂一些,整个过程出现异常是会执行这个方法,这里不只是联网的过程,还包括对返回数据处理上的异常,比如json解析失败等。如果发生异常就不会再走onComplete(),所以同样需要判断加载框的显示。下面的是对一些异常的处理,这里只处理了一些网络方面的异常,可以根据需求添加异常判断,这里处理的异常只是出现意外的异常。这里还自定义了一个异常ApiException,抛出这个异常都是业务上的问题,比如空数据,格式不对等等,这个是根据返回的状态值判断的,方便统一处理错误信息。

public class RxFunction<T> implements Function<HttpResult<T>, T> {
    @Override
    public T apply(@NonNull HttpResult<T> httpResult) throws Exception {
        int retCode = httpResult.getRetCode();
        if (retCode != 200) {
            switch (retCode) {
                case 21001:
                    throw new ApiException("查询的日期格式错误,格式:yyyy-MM-dd");
              //  case 2111:
              //      throw ........
            }
        }
        return httpResult.getResult();
    }
}

这就是这个ApiException产生的地方,也是这句话map(new RxFunction<T>())用到的逻辑,这个是在Retrofit2解析json后得到HttpResult<T>,转化成Tmap操作符,在转化过程中根据HttpResult的状态值,判断是返回T还是抛出ApiException异常,这里可以根据返回状态值添加不同的错误提示信息,异常会在RxObserveronError(Throwable e)被捕捉,统一传到View层去处理。

compose(RxSchedulers.<Calendar>io_main())这句话是切换了一下线程,等同于这两句

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

执行在io线程,表现在安卓主线程。RxSchedulers完整的代码:

public class RxSchedulers {

    public static <T> ObservableTransformer<T, T> io_main() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply( Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

还有一个类RxManager,这个类是用来管理订阅的,在RxObserver中,执行onSubscribe方法时,把参数Disposable添加进了RxManager中,看一下RxManager对应的add方法:

    public void add(String key, Disposable disposable) {
        Set<String> keySet = map.keySet();
        if (keySet.contains(key)) {
            CompositeDisposable compositeDisposable = map.get(key);
            compositeDisposable.add(disposable);
        } else {
            CompositeDisposable compositeDisposable = new CompositeDisposable();
            compositeDisposable.add(disposable);
            map.put(key,compositeDisposable );
        }
    }

这里面,给每一个key初始化了一个CompositeDisposable并存入map,把这个Disposable 加入到对应的CompositeDisposable 中。

RxManager中还有一个方法clear(String key)

    public void clear(String key) {
        Set<String> keySet = map.keySet();
        if (keySet.contains(key)) {
            CompositeDisposable compositeDisposable = map.get(key);
            compositeDisposable.clear();
            map.remove(key);
        }
    }

根据key得到对应的CompositeDisposable ,并执行compositeDisposable.clear()来取消compositeDisposable中所有的订阅关系。这个RxManagerclear方法建议放在BaseActivityonDestroy()方法中,这也是为什么前面说的建议这个key采用包名+类名的方式的原因,当这个类销毁的时候,该类中所有的联网订阅关系都会被取消,避免内存泄漏。为保证这个map唯一,RxManager采用了单利模式:

    private static RxManager rxManager;
    private Map<String, CompositeDisposable> map;

    private RxManager() {
        map = new HashMap<>();
    }

    public static RxManager getInstance() {
        if (rxManager == null) {
            rxManager = new RxManager();
        }
        return rxManager;
    }

BaseActivity的onDestroy:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RxManager.getInstance().clear(TAG);
    }

以上就是封装的netWork module的基本使用。
最能体现Retrofit2+Rxjava2优势的自然是MVP的结构,下面就演示一下MVP的写法。

MVP

这里同样不讨论mvp的基础知识,也不讨论几种mvp哪种写法更正宗,我觉得只要是代码逻辑清楚,耦合性低,都是好的代码架构。
这里为求简便,PresenterModel没有写成接口,直接写的各自的实现类。先看Model层:

public class MVPModel {

    public Observable<Calendar> getCalendar(String date) {
        return Api.getDefaultService().calendarBean(date).map(new RxFunction<Calendar>()).compose(RxSchedulers.<Calendar>io_main());
    }
}

Model切换线程,对请求的数据进行解析,并转化成我们需要的Observable<Calendar>对象。再看View层:

public interface MVPView {

    void setResult(Calendar calendar);

    void onError(int whichRequest ,Throwable t);

    void onStartLoading(int whichRequest);

    void onEndLoading(int whichRequest);

}

View需要写成接口,让对应的Activity或者Fragment去实现,View中除了setResultonError两个必要的方法外,又写了onStartLoadingonEndLoading两个方法,这两个方法是分别在联网操作开始和结束时候的回调,已满足其他需要在操作开始和结束做的操作,比如不用RxObserver中的加载框,需要其他加载框的,如如SwipeRefreshLayout。再看Presenter

public class MVPPresenter {

    private MVPModel mvpModel;
    private MVPView mvpView;

    public MVPPresenter(MVPView mvpView) {
        this.mvpView = mvpView;
        mvpModel = new MVPModel();
    }

    public void getCalendar(Context context,String date, String key,int whichRequest ,boolean isShowDialog) {

        mvpModel.getCalendar(date).subscribe(new RxObserver<Calendar>(context,key,whichRequest,isShowDialog) {
            @Override
            public void onStart(int whichRequest) {
                super.onStart(whichRequest);
                mvpView.onStartLoading(whichRequest);
            }

            @Override
            public void onSuccess(int whichRequest, Calendar calendar) {
                mvpView.onEndLoading(whichRequest);
                mvpView.setResult(calendar);
            }

            @Override
            public void onError(int whichRequest, Throwable e) {
                mvpView.onError(whichRequest, e);
                mvpView.onEndLoading(whichRequest);
            }
        });
    }
}

Presenter同时持有ModelView的对象,把从Model得到的数据传到View去处理。最后看View的实现类MVPActivity

public class MVPActivity extends BaseActivity implements MVPView{

    TextView tv_;
    private MVPPresenter mvpPresenter;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_mvp;
    }

    @Override
    protected void initData() {
        tv_ = (TextView) findViewById(R.id.tv_);
        mvpPresenter = new MVPPresenter(this);
    }

    public void request(View view) {
        mvpPresenter.getCalendar(this, "2018-10-01", TAG, 0, false);
    }


    @Override
    public void setResult(Calendar calendar) {
        tv_.setText(calendar.getWeekday());
    }

    /**
     * 如果有多个请求可根据whichRequest处理不同请求的异常
     * @param whichRequest
     * @param t
     */
    @Override
    public void onError(int whichRequest, Throwable t) {

    }

    /**
     * 如果不使用RxObserver自带的Dialog,可以在RxObserver中设置false,
     * 在onStartLoading和onEndLoading设置需要其他dialog的显示和消失,如SwipeRefreshLayout
     * @param whichRequest
     */
    @Override
    public void onStartLoading(int whichRequest) {

    }

    @Override
    public void onEndLoading(int whichRequest) {

    }
}

整个Activity看起来非常清爽,由mvpPresenter发出getCalendar()的请求,在setResult中获得结果,onError中处理异常,onStartLoadingonEndLoading处理加载框,如果一个页面需要多个请求,可以给RxObserver传入不同的whichRequest值,根据whichRequest判断在对应的方法中做不同的操作。

以上就是封装的这个Retrofit2+Rxjava2的全部内容了,例子中对Retrofit的配置不是很多,如果需要缓存等其他配置,可自行添加。

代码地址:https://github.com/yannecer/Retrofit2-Rxjava2

参考文章:RxJava 与 Retrofit 结合的最佳实践

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值