基于 RxJava2+Retrofit+RxCache 的网络请求封装

yangsaiddd 2年前
   <p>这套框架来源于现有项目,这几天开了新项目正好用到顺手就把这套框架抽出来了,也方便以后使用。目前网上对Rxjava2+Retrofit2的封装真是太多了,但是大体思路都是一样的,而且好多代码都具有相似性,这套其实也不例外,大家可选择性使用。</p>    <p>首先我们先不说封装思路,先说说这套框架都都具有哪些功能及如何使用;</p>    <h3>功能</h3>    <p>1 使用RxCache缓存机制,可自定义缓存过期时间,及数据分页缓存等功能。</p>    <p>2 统一的请求错误处理;</p>    <p>3 统一的网络状态判断处理;</p>    <p>4 基于HttpLoggingInterceptor的请求日志打印。</p>    <p>以上就是这套框架可以实现的功能,框架中并没有像其他的一样封装了ProgressBar</p>    <p>,因为每个项目不同,ProgressBar的样式需求并不一样,就算同一个项目中下拉刷新和普通加载可能也不一样,所以需要使用的小伙伴自己定义ProgressBar。</p>    <p>此外这套框架使用了RxCache实现缓存,而并不是通过OKHttp缓存,所以这套框架对服务器没有任何要求,不需要定义好Header之类的东西。如果你对RxCache不熟悉,可以看看这篇文章,或者RxCache的 <a href="/misc/goto?guid=4959748090594833394" rel="nofollow,noindex">官网</a> 。不知道这么牛的框架为啥start目前才1000多呢,不说废话了,来看使用方式。</p>    <h3>使用方式</h3>    <p>调用以下代码完成网络请求</p>    <pre>  <code class="language-java">//声明监听  HttpSubscriber mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {          @Override          public void onSuccess(TestBean tb) {          }          @Override          public void onError(int code,String errorMsg) {          }      });  //发起请求      HttpManager.getInstance().getDatas(mHttpObserver,1,10,"json",true);    //取消请求   mHttpObserver.unSubscribe();</code></pre>    <p>看起来是不是很简单?</p>    <p>下面来说说上面的代码是如何完成网络数据请求的。先来看看这个框架的结构</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8093aba589b7a7839cedb9d7e4ede167.png"></p>    <p>仅仅只有七个类而已。</p>    <p>简单介绍一下个各类的职责</p>    <p>ApiResponse——封装的返回数据模板(里面的error_code,reason,result名称需要你跟后台的小伙伴对应好,通常情况下error_code代码状态码,reason为成功或失败的提示信息,result中为具体的数据,由于数据格式未知所以使用泛型代表)</p>    <p>ApiService——Retrofit的数据请求接口。注意一下每个方法的返回值类型。(我们真正需要的是TestBean中的数据它必须被ApiResponse包装,最后返回Observable类型)</p>    <p>CacheProvider——RxCache的缓存接口,注意它的第一个参数类型必须和Retrofit数据请求接口的返回值类型一样。</p>    <p>OnResultCallBack——请求成功或失败的回调。</p>    <p>ApiException——公共的请求错误处理类。</p>    <p>HttpSubscriber——公共的请求订阅者。</p>    <p>HttpManager——发起请求的管理类。</p>    <p>首先是引入的各种库文件</p>    <pre>  <code class="language-java">//Rxjava2  compile 'io.reactivex.rxjava2:rxjava:2.0.7'  compile 'io.reactivex.rxjava2:rxandroid:2.0.1'  //Retrofit2  compile 'com.squareup.retrofit2:retrofit:latest.release'  compile 'com.squareup.retrofit2:converter-gson:latest.release'  compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'  //RxCache  compile "com.github.VictorAlbertos.RxCache:runtime:1.8.0-2.x"  compile 'com.github.VictorAlbertos.Jolyglot:gson:0.0.3'  //Okhttp-interceptor  compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'</code></pre>    <p>HttpManager的初始化</p>    <pre>  <code class="language-java">public static HttpManager getInstance() {      if (instance == null) {          synchronized (HttpManager.class) {              if (instance == null) {                  instance = new HttpManager();              }          }      }      return instance;  }</code></pre>    <p>单例模式的HttpManager,来看HttpManager的构造函数</p>    <pre>  <code class="language-java">private HttpManager() {      HttpLoggingInterceptor.Level level= HttpLoggingInterceptor.Level.BODY;      HttpLoggingInterceptor loggingInterceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {          @Override          public void log(String message) {              Log.i("HttpManager",message);          }      });      loggingInterceptor.setLevel(level);      OkHttpClient.Builder builder = new OkHttpClient.Builder();      builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)              .retryOnConnectionFailure(true)              .addInterceptor(loggingInterceptor);      OkHttpClient okHttpClient = builder.build();        mRetrofit = new Retrofit.Builder()              .addConverterFactory(GsonConverterFactory.create())              .addCallAdapterFactory(RxJava2CallAdapterFactory.create())              .baseUrl(Constant.BASE_URL)              .client(okHttpClient)              .build();        cacheProvider = new RxCache.Builder()              .persistence(mContext.getFilesDir(), new GsonSpeaker())              .using(CacheProvider.class);        mApiService = mRetrofit.create(ApiService.class);  }</code></pre>    <p>在构造函数中,首先我们通过HttpLoggingInterceptor设置了拦截器,并通过addInterceptor方法设置给 OkHttpClient.Builder。然后是构建OkHttpClient.Builder及okHttpClient ,再然后是Retrofit的初始化。</p>    <p>下面接着是RxCache的初始化,并在其中设置了缓存目录,mContext为ApplicationContext,由init方法传入。</p>    <pre>  <code class="language-java">public class App extends Application {      @Override      public void onCreate() {          super.onCreate();          HttpManager.init(this);//不做任何操作仅仅是缓存一下Application引用      }  }</code></pre>    <p>那么HttpManager是如何完成一个网络请求的呢?</p>    <p>1 ApiService中声明请求接口</p>    <pre>  <code class="language-java">@FormUrlEncoded  @POST("query?key=7c2d1da3b8634a2b9fe8848c3a9edcba")  Observable<ApiResponse<TestBean>> getDatas(@Field("pno") int pno, @Field("ps") int ps, @Field("dtype") String dtype);</code></pre>    <p>2CacheProvider中声明缓存接口(如果需要缓存就写)</p>    <pre>  <code class="language-java">@LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES)  Observable<ApiResponse<TestBean>> getDatas(Observable<ApiResponse<TestBean>> oRepos, EvictProvider evictDynamicKey);</code></pre>    <p>注意看注解,可以自定义缓存过期时间。(EvictProvider 参数是设置是否需要对请求的数据进行缓存,具体可以看这里)同时,第一个参数一定要是Observable</p>    <p>> 我们在ApiService中声明好的getDatas方法的返回值。</p>    <p>3 HttpManager声明对应的请求方法</p>    <p>方法有两种,带缓存的方式</p>    <p> </p>    <pre>  <code class="language-java">public void getDatasWithCache(Observer<TestBean> subscriber, int pno, int ps, String dtype, boolean update) {      toSubscribe(cacheProvider.getDatas(mApiService.getDatas(pno, ps,dtype),new EvictProvider(update)), subscriber);  }</code></pre>    <p>不带缓存的方式</p>    <pre>  <code class="language-java">public void getDatasNoCache(Observer<TestBean> subscriber, int pno, int ps, String dtype) {      toSubscribe(mApiService.getDatas(pno, ps,dtype), subscriber);  }</code></pre>    <p>看到没不带缓存的方法只是没有用cacheProvider.getDatas()方法包裹,同时少了一个控制是否更新的参数update。</p>    <p>再来看一下toSubscribe()方法的实现</p>    <pre>  <code class="language-java">private <T> void toSubscribe(Observable<ApiResponse<T>> o, Observer<T> s) {      o.subscribeOn(Schedulers.io())              .map(new Function<ApiResponse<T>, T>() {                  @Override                  public T apply(@NonNull ApiResponse<T> response) throws Exception {                      int code=Integer.parseInt(response.getCode());                      if (code!=Constant.SUCCESS_CODE) {                          throw new ApiException(code, response.getMsg());                      } else {                          if (response.getDatas() == null) {                              return (T) response.getMsg();                          } else {                              return response.getDatas();                          }                      }                  }              })              .unsubscribeOn(Schedulers.io())              .observeOn(AndroidSchedulers.mainThread())              .subscribe(s);  }</code></pre>    <p>借助Rxjava的map方法完成数据的进一步转换,注意SUCCESS_CODE,这个值是数据返回成功时的状态码,需要你和后台小伙伴定义好,一般情况下都为0。如果code!=SUCCESS_CODE,那么出错返回的code的和message都会抛给ApiException,在ApiException中的getApiExceptionMessage方法你可以根据具体code重新定义不同的message,或者使用传入进来的message,通常情况下这个类不需要修改,如果需要客户端根据code自定义message那么就按照上面所的方式修改即可。最后所有的message都会抛给HttpSubscriber的onError方法</p>    <pre>  <code class="language-java">@Override  public void onError(Throwable e) {      if (e instanceof CompositeException) {          CompositeException compositeE=(CompositeException)e;          for (Throwable throwable : compositeE.getExceptions()) {              if (throwable instanceof SocketTimeoutException) {                  mOnResultListener.onError(ApiException.Code_TimeOut,ApiException.SOCKET_TIMEOUT_EXCEPTION);              } else if (throwable instanceof ConnectException) {                  mOnResultListener.onError(ApiException.Code_UnConnected,ApiException.CONNECT_EXCEPTION);              } else if (throwable instanceof UnknownHostException) {                  mOnResultListener.onError(ApiException.Code_UnConnected,ApiException.CONNECT_EXCEPTION);              } else if (throwable instanceof RxCacheException) {                  //缓存异常暂时不做处理              }  else if (throwable instanceof MalformedJsonException) {                  mOnResultListener.onError(ApiException.Code_MalformedJson,ApiException.MALFORMED_JSON_EXCEPTION);              }          }      }else {          mOnResultListener.onError(ApiException.Code_Other,e.getMessage());      }  }</code></pre>    <p>在这个方法中也统一处理了网络问题。注意看不同的网络状态返回的状态码是不一样的。</p>    <p>SocketTimeoutException 网络超时 1000</p>    <p>ConnectException 链接异常 1001</p>    <p>UnknownHostException Host异常 1001</p>    <p>MalformedJsonException 解析异常 1020</p>    <p>其他错误信息统一返回 1003(因为code在此基本没什么用了,重要的是错误提示信息)</p>    <p>最终执行到</p>    <pre>  <code class="language-java">mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {          @Override          public void onSuccess(TestBean tb) {          }          @Override          public void onError(int code,String errorMsg) {          }      });</code></pre>    <p>中的onError方法,在这里我们根据不同的code展示不同的界面即可(例如常见的网络错误界面),或者通过Toast等其他方式给用户提示。</p>    <p>再回到toSubscribe方法,如果数据返回成功了,即code==SUCCESS_CODE,那么数据会返回给HttpSubscriber的onNext方法</p>    <pre>  <code class="language-java">@Override  public void onNext(T t) {      if (mOnResultListener != null) {          mOnResultListener.onSuccess(t);      }  }</code></pre>    <p>onNext中调用OnResultCallBack的onSuccess方法,把数据传递到</p>    <pre>  <code class="language-java">mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {          @Override          public void onSuccess(TestBean tb) {          }          @Override          public void onError(int code,String errorMsg) {          }      });</code></pre>    <p>中的onSuccess中,在这里我们把数据展示给用户即可。</p>    <p>HttpSubscriber的另一项任务就是取消数据请求操作。</p>    <p>在onSubscribe中通过一个全局的mDisposable记录当前的Disposable</p>    <pre>  <code class="language-java">@Override  public void onSubscribe(Disposable d) {      mDisposable = d;  }</code></pre>    <p>在需要取消的地方调用unSubscribe()即可</p>    <pre>  <code class="language-java">public void unSubscribe() {      if (mDisposable != null && !mDisposable.isDisposed()) {          mDisposable.dispose();      }  }</code></pre>    <p>下面运行一下程序</p>    <pre>  <code class="language-java">mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {          @Override          public void onSuccess(TestBean tb) {              for (TestBean.ListBean bean : tb.getList()) {                  result+=bean.toString();              }              resultTv.setText(result);          }          @Override          public void onError(int code,String errorMsg) {              resultTv.setText("onError: code:"+code+"  errorMsg:"+errorMsg );          }      });</code></pre>    <p>这里成功后通过TextView显示一下数据,失败时也显示一下错误信息和code;</p>    <p>首先是不使用缓存</p>    <pre>  <code class="language-java">HttpManager.getInstance().getDatasNoCache(mHttpObserver,1,10,"json");</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7b2243a9187ae10a56182654ad49b132.gif"></p>    <p>数据请求成功,再来测试断网情况</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8bdfc5106bd6e0cf16b2a297ee1ec4f1.gif"></p>    <p>断网后返回了错误码1003。</p>    <p>下面测试缓存</p>    <p>设置一个5分钟的缓存</p>    <pre>  <code class="language-java">@LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES)  Observable<ApiResponse<TestBean>> getDatas(Observable<ApiResponse<TestBean>> oRepos, EvictProvider evictDynamicKey);     HttpManager.getInstance().getDatasWithCache(mHttpObserver,1,10,"json",false);</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/521d9d055a68383f926ff899904dbb93.gif"></p>    <p>可以看到第一次请求产生了网络流量,说明数据来自网络,而后的几次请求均没有产生流量,说明数据来自本地缓存。</p>    <p>好了这套框架就介绍到这里吧,有需要的可以在这里下载</p>    <p><a href="/misc/goto?guid=4959748090706255361" rel="nofollow,noindex">https://github.com/shiweibsw/EasyHttp</a></p>    <h2>如何使用?</h2>    <p>1 build.gradle中导入插件</p>    <p>2 将http包下所有内容拷贝到你的工程即可。</p>    <h2>下一步的计划</h2>    <p>1 retrofit 接口类封装基本的get和post请求;</p>    <p>2 支持以插件的形式导入工程。</p>    <p> </p>    <p>来自:https://juejin.im/post/58fea9a0da2f60005dd1e76a</p>    <p> </p>