基于 RxJava2+Retrofit+RxCache 的网络请求封装
yangsaiddd
7年前
<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>