前言
开发过程中,一般都会对网络框架进行再次封装,以配置各种参数,并适合自己的编码风格。各种网络框架比较下来,还是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的配置不是很多,如果需要缓存等其他配置,可自行添加。
随时随地看视频