一步一步带你认识MVP+Retrofit+Rxjava并封装(二)

一步一步带你认识MVP+Retrofit+Rxjava并封装(一)

一步一步带你认识MVP+Retrofit+Rxjava并封装(二)

序言:继续上周咱们没讲完的,上周咱们一起撸了一把MVP,今天跟着LZ继续撸Retrofit+RxJava,这俩算得上当下最流行的网络框架了,数据说话,有空的话你们也去github上搜索一波:

我们可以看出来,单论star数的话,Retrofit和OkHttp是巨头般的存在,网上关于retrofit的文章早已数不胜数了,这里我也不详细介绍了,下面就直接进入主题。

在开始之前,先介绍大家去学习一下RxJava,这个是真的灰常有用的一个库,Rx系列的都很不错,这里有两个版本,他们之间的方法稍微改变了一下,其他都差不多:

给初学者的RxJava2.0教程系列

给Android开发者的RxJava1.0详解

如果你上周没跟着LZ一起撸的话,那么请移步:
一步一步带你认识MVP+Retrofit+Rxjava并封装(一)

1、导包:

//网络请求 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'复制代码

2、封装:

我们现来看一下,一个完整的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() {
       @Override
       public void onSubscribe(Disposable d) {
               //为请求提供一个取消的手段
       }

       @Override
       public void onNext(LoginResponse value) {
               //请求成功
       }

       @Override
       public void onError(Throwable e) {
           //请求出错
       }

       @Override
       public void onComplete() {
           //请求完成
       }
   });复制代码

考虑到每次请求接口的时候都需要去实例化一个Retrofit对象,而且每次都需要用RxJava来进行线程的切换,因此我就想到把它们都写到一个基类里面去。

 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中:

 protected  void toSubscribe(Observable observable, Observer observer) {
    observable.subscribeOn(Schedulers.io())    // 指定subscribe()发生在IO线程
            .observeOn(AndroidSchedulers.mainThread())  // 指定Subscriber的回调发生在io线程
            .timeout(DEFAULT_TIME, TimeUnit.SECONDS)    //重连间隔时间
            .retry(RETRY_TIMES)
//          .repeatWhen(new Function, ObservableSource>() {
//                @Override
//                public ObservableSource apply(@NonNull Observable objectObservable) throws Exception {
//                    return null;
//                }
//           })
            .subscribe(observer);   //订阅
}复制代码 
   

这样每次我们组装好ObservableObserver之后就调用这个方法进行订阅就好了。这里我有一个困惑,已经很久了,希望知道的读者能帮忙解惑,重写retryWhen的时候,如何根据错误类型进行重试。讲到这里可能有人就要问了,LZ你不还是没有讲是怎么进行网络请求的吗?大兄弟别急,我这就告诉你,它是通过自定义接口的形式来进行网络请求的,好吧,说了好像也白说,换个场景你自个去深入了解去吧:
Retrofit网络请求框架的基本使用

好了,接着我们下面的封装:

被观察者已经当作接口被我们处理掉了,那么下面我们重点关注观察者;很久之前我老大跟我讲网络请求封装这一块,他当时说我们只关注请求成功的数据,其他的不需要特别关注;首先,我们得有一套统一的回调样式,如下:

{
    "status":1,
    "data":T
    "msg":"success"
}复制代码

由于我们这边都把返回的json数据都转成BaseResponse格式了,如果你们回调的数据格式不统一的话,那就去找后端撕逼去吧;然后我们只需要重写Observer就行了,Observer接口中有四个方法,上面例子中我们简单介绍了一下,它们的执行顺序分别是onSubscribe——>onNext——>onComplete(onError),这里需要简单提一下,onCompleteonError方法二者不会同时都执行,具体来看一下LZ封装的:

public abstract class BaseObserver implements Observer {

    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 extends BaseObserver {

    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 implements Function, T> {

    @Override
    public T apply(BaseResponse response) throws Exception {
        if (!response.isRequestSuccess()) {
            throw new ApiException(response.getStatus(), String.valueOf(response.getMsg()));
        }
        return response.getData();
    }
}复制代码

相信看完方法的实现大家应该知道这个是干什么用的了,没错,这个方法就是将BaseResponse转换成T,因为我们只关注成功的数据,而且只关注data里面的数据,由于返回的数据是BaseResponse,而我们需要关注的数据是T,所以在这里需要转换一下,然后判断请求是否成功就行了。

#####最后的最后,上面我提到一个问题,就是如何重写retryWhen方法,根据错误类型来对请求进行重试操作,这里给出最终的代码(这里LZ是想在网络出错或者连接超时的时候进行重试):

.retryWhen(new Function, ObservableSource>() {

                private int mRetryCount;

                @Override
                public ObservableSource apply(@NonNull Observable throwableObservable) throws Exception {
                    return throwableObservable.flatMap(new Function>() {
                        @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);
                        }
                    });
                }
            })复制代码

好了,到这里基本的网络请求封装就完成了,如果你有更好的方法,请私信我一起交流,同时感谢以上引用到博客的博主。最后的最后,放出最后的代码,欢迎starfork

MVP 主工程代码

MVP module工程代码

公众号:Android技术经验分享

你可能感兴趣的:(一步一步带你认识MVP+Retrofit+Rxjava并封装(二))