在开始以前,先介绍你们去学习一下RxJava,这个是真的灰常有用的一个库,Rx系列的都很不错,这里有两个版本,他们之间的方法稍微改变了一下,其余都差很少:java
给初学者的RxJava2.0教程系列react
给Android开发者的RxJava1.0详解android
若是你上周没跟着LZ一块儿撸的话,那么请移步:
一步一步带你认识MVP+Retrofit+Rxjava并封装(一)git
一、导包:github
//网络请求 retrofit+okhttp+gson
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'复制代码
二、封装:json
咱们现来看一下,一个完整的
Retrofit+Rxjva
的请求:后端
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
builder.connectTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
//设置拦截器
builder.addInterceptor(new BasicParamsInterceptor.Builder().addParamsMap(getCommonMap()).build());
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
OkHttpClient okHttpClient = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
ApiService api=retrofit.create(ApiService.class);
api.login(username,password)
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果
.subscribe(new Observer<LoginResponse>() {
@Override
public void onSubscribe(Disposable d) {
//为请求提供一个取消的手段
}
@Override
public void onNext(LoginResponse value) {
//请求成功
}
@Override
public void onError(Throwable e) {
//请求出错
}
@Override
public void onComplete() {
//请求完成
}
});复制代码
考虑到每次请求接口的时候都须要去实例化一个Retrofit对象,并且每次都须要用RxJava来进行线程的切换,所以我就想到把它们都写到一个基类里面去。api
public abstract class BaseRetrofit {
protected Retrofit mRetrofit;
private static final int DEFAULT_TIME = 10; //默认超时时间
private final long RETRY_TIMES = 1; //重订阅次数
public BaseRetrofit() {
//建立okHttpClient
if (null == mRetrofit) {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
builder.connectTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
//设置拦截器
builder.addInterceptor(new BasicParamsInterceptor.Builder().addParamsMap(getCommonMap()).build());
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
OkHttpClient okHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(HttpServletAddress.getInstance().getServletAddress())
.client(okHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
}复制代码
而后结合咱们上周讲的
MVP
,让BaseModel
继承它,而后调用方法进行请求,上周咱们尚未细节讲它是怎么样进行网络请求的,回到刚才那个完整的请求的例子,能够看到,这里发起请求须要两个东西,一个Observer
,另外一个是api.login()
的返回值Observable
,这就是大佬们口中说的观察者和被观察者,他们之间有一个很微妙的关系,叫订阅;被观察者负责网络请求,观察者负责观察网络请求的回调,每发生一次接口请求,都会有订阅发生,因此在这里我把订阅公共的逻辑放到了BaseRetrofit
中:bash
protected <T> void toSubscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.io()) // 指定subscribe()发生在IO线程
.observeOn(AndroidSchedulers.mainThread()) // 指定Subscriber的回调发生在io线程
.timeout(DEFAULT_TIME, TimeUnit.SECONDS) //重连间隔时间
.retry(RETRY_TIMES)
// .repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
// @Override
// public ObservableSource<?> apply(@NonNull Observable<Object> objectObservable) throws Exception {
// return null;
// }
// })
.subscribe(observer); //订阅
}复制代码
这样每次咱们组装好
Observable
和Observer
以后就调用这个方法进行订阅就行了。这里我有一个困惑,已经好久了,但愿知道的读者能帮忙解惑,重写retryWhen的时候,如何根据错误类型进行重试
。讲到这里可能有人就要问了,LZ
你不仍是没有讲是怎么进行网络请求的吗?大兄弟别急,我这就告诉你,它是经过自定义接口的形式来进行网络请求的,好吧,说了好像也白说,换个场景你自个去深刻了解去吧:
Retrofit网络请求框架的基本使用网络
好了,接着咱们下面的封装:
被观察者已经看成接口被咱们处理掉了,那么下面咱们重点关注观察者;好久以前我老大跟我讲网络请求封装这一块,他当时说咱们只关注请求成功的数据,其余的不须要特别关注;首先,咱们得有一套统一的回调样式,以下:
{
"status":1,
"data":T
"msg":"success"
}复制代码
因为咱们这边都把返回的
json
数据都转成BaseResponse<T>
格式了,若是大家回调的数据格式不统一的话,那就去找后端撕逼去吧;而后咱们只须要重写Observer
就好了,Observer
接口中有四个方法,上面例子中咱们简单介绍了一下,它们的执行顺序分别是onSubscribe——>onNext——>onComplete(onError)
,这里须要简单提一下,onComplete
和onError
方法两者不会同时都执行,具体来看一下LZ封装的:
public abstract class BaseObserver<T> implements Observer<T> {
private static final String TAG = "BaseObserver";
protected abstract void onBaseError(Throwable t);
protected abstract void onBaseNext(T data);
protected abstract boolean isNeedProgressDialog();
protected abstract String getTitleMsg();
private ProgressDialogHandler mProgressDialogHandler;
private BaseImpl mBaseImpl;
public BaseObserver(BaseImpl baseImpl) {
mBaseImpl = baseImpl;
if (null != mBaseImpl) {
if (null == mProgressDialogHandler) {
mProgressDialogHandler = new ProgressDialogHandler(baseImpl.getContext(), true);
}
}
}
private void showProgressDialog() {
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG, getTitleMsg()).sendToTarget();
}
}
private void dismissProgressDialog() {
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
mProgressDialogHandler = null;
}
}
@Override
public void onSubscribe(Disposable d) {
//显示进度条
if (isNeedProgressDialog()) {
showProgressDialog();
}
if (null != mBaseImpl) {
if (null != d) {
mBaseImpl.addDisposable(d);
}
}
}
@Override
public void onNext(T value) {
//成功
Log.d(TAG, "http is onNext");
if (null != value) {
onBaseNext(value);
}
}
@Override
public void onError(Throwable e) {
//关闭进度条
Log.e(TAG, "http is onError");
if (isNeedProgressDialog()) {
dismissProgressDialog();
}
onBaseError(e);
}
@Override
public void onComplete() {
//关闭进度条
if (isNeedProgressDialog()) {
dismissProgressDialog();
}
}
}复制代码
这里考虑到有些界面须要进度框,因此我把这一部分也整合到观察者里面,这里根据外面调用的地方有没有设置
Title
来判断是否显示进度框,而后再进行相应的回调,进度框使用的是系统的ProgressDialog
,固然了,你也能够自定义一个进度框样式,详细见demo
。前面咱们说到咱们只关心成功的数据,失败的数据咱们须要在内部处理掉,即再封装一层,吃掉onBaseError
:
public abstract class CygBaseObserver<T> extends BaseObserver<T> {
private static final String TAG = "CygBaseObserver";
private boolean isNeedProgress;
private String titleMsg;
public CygBaseObserver() {
this(null, null);
}
public CygBaseObserver(BaseImpl base) {
this(base, null);
}
public CygBaseObserver(BaseImpl base, String titleMsg) {
super(base);
this.titleMsg = titleMsg;
if (TextUtils.isEmpty(titleMsg)) {
this.isNeedProgress = false;
} else {
this.isNeedProgress = true;
}
}
@Override
protected boolean isNeedProgressDialog() {
return isNeedProgress;
}
@Override
protected String getTitleMsg() {
return titleMsg;
}
@Override
protected void onBaseError(Throwable t) {
StringBuffer sb = new StringBuffer();
sb.append("请求失败:");
if (t instanceof NetworkErrorException || t instanceof UnknownHostException || t instanceof ConnectException) {
sb.append("网络异常");
} else if (t instanceof SocketTimeoutException || t instanceof InterruptedIOException || t instanceof TimeoutException) {
sb.append("请求超时");
} else if (t instanceof JsonSyntaxException) {
sb.append("请求不合法");
} else if (t instanceof JsonParseException
|| t instanceof JSONException
|| t instanceof ParseException) { // 解析错误
sb.append("解析错误");
} else if (t instanceof ApiException) {
if (((ApiException) t).isTokenExpried()) {
sb.append("Token出错");
}
} else {
FRToast.showToastSafe(t.getMessage());
return;
}
Log.e(TAG, "onBaseError: " + sb.toString());
FRToast.showToastSafe(sb.toString());
}
}复制代码
最开始的例子当中有一个
Disposable
的概念,这个是用来切断观察者与被观察者之间的关系的,每次请求都会产生一个响应的Disposable
,因此这里我用了一个接口BaseImpl
的形式来回收它,在产生的地方收集它,在BaseActivity的onDestroy中来回收它,详细的请参见demo
;好了,到这里咱们的封装就完成了90%了,让咱们回到上一次博客当中,组装Observable
的时候咱们还进行了一个map
操做:![]()
这里map就是进行一个中间的操做,这个操做叫作变换,咱们来看一下HttpFunction的实现是怎样的:
public class HttpFunction<T> implements Function<BaseResponse<T>, T> {
@Override
public T apply(BaseResponse<T> response) throws Exception {
if (!response.isRequestSuccess()) {
throw new ApiException(response.getStatus(), String.valueOf(response.getMsg()));
}
return response.getData();
}
}复制代码
相信看完方法的实现你们应该知道这个是干什么用的了,没错,这个方法就是将
BaseResponse<T>
转换成T
,由于咱们只关注成功的数据,并且只关注data
里面的数据,因为返回的数据是BaseResponse<T>
,而咱们须要关注的数据是T
,因此在这里须要转换一下,而后判断请求是否成功就好了。
#####最后的最后,上面我提到一个问题,就是如何重写retryWhen方法,根据错误类型来对请求进行重试操做,这里给出最终的代码(这里LZ是想在网络出错或者链接超时的时候进行重试):
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
private int mRetryCount;
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
if ((throwable instanceof NetworkErrorException
|| throwable instanceof ConnectException
|| throwable instanceof SocketTimeoutException
|| throwable instanceof TimeoutException) && mRetryCount < 3) {
mRetryCount++;
return Observable.timer(2000, TimeUnit.MILLISECONDS);
}
return Observable.error(throwable);
}
});
}
})复制代码
好了,到这里基本的网络请求封装就完成了,若是你有更好的方法,请私信我一块儿交流,同时感谢以上引用到博客的博主。最后的最后,放出最后的代码,欢迎star
和fork