前言
开发过程中,一般都会对网络框架进行再次封装,以配置各种参数,并适合自己的编码风格。各种网络框架比较下来,还是Retrofit2+Rxjava2看着最爽,今天把这个东西整理了一下,发出来,示例给出了一般写法和MVP的写法。
Retrofit2+Rxjava2
Retrofit2
和Rxjava2
基础的东西就不说了,直接进入主题。我们的需求是:
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(); }
参数说明: key
:key
是用来区分不同类中联网的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>
,转化成T
的map
操作符,在转化过程中根据HttpResult
的状态值,判断是返回T
还是抛出ApiException
异常,这里可以根据返回状态值添加不同的错误提示信息,异常会在RxObserver
的onError(Throwable e)
被捕捉,统一传到View层去处理。
compose(RxSchedulers.<Calendar>io_main())
这句话是切换了一下线程,等同于这两句
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())12
执行在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中所有的订阅关系。这个RxManager
的clear
方法建议放在BaseActivity
的onDestroy()
方法中,这也是为什么前面说的建议这个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
哪种写法更正宗,我觉得只要是代码逻辑清楚,耦合性低,都是好的代码架构。
这里为求简便,Presenter
和Model
没有写成接口,直接写的各自的实现类。先看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
中除了setResult
和onError
两个必要的方法外,又写了onStartLoading
和onEndLoading
两个方法,这两个方法是分别在联网操作开始和结束时候的回调,已满足其他需要在操作开始和结束做的操作,比如不用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
同时持有Model
和View
的对象,把从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
中处理异常,onStartLoading
和onEndLoading
处理加载框,如果一个页面需要多个请求,可以给RxObserver
传入不同的whichRequest
值,根据whichRequest
判断在对应的方法中做不同的操作。
以上就是封装的这个Retrofit2+Rxjava2
的全部内容了,例子中对Retrofit
的配置不是很多,如果需要缓存等其他配置,可自行添加。
共同学习,写下你的评论
评论加载中...
作者其他优质文章