# XHttp2 **Repository Path**: yongshengwang/XHttp2 ## Basic Information - **Project Name**: XHttp2 - **Description**: 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。 - **Primary Language**: Android - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 42 - **Created**: 2020-12-11 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # XHttp2 [![](https://jitpack.io/v/xuexiangjys/XHttp2.svg)](https://jitpack.io/#xuexiangjys/XHttp2) [![api](https://img.shields.io/badge/API-14+-brightgreen.svg)](https://android-arsenal.com/api?level=14) [![I](https://img.shields.io/github/issues/xuexiangjys/XHttp2.svg)](https://github.com/xuexiangjys/XHttp2/issues) [![Star](https://img.shields.io/github/stars/xuexiangjys/XHttp2.svg)](https://github.com/xuexiangjys/XHttp2) 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),体验一下吧! 在提issue前,请先阅读[【提问的智慧】](https://xuexiangjys.blog.csdn.net/article/details/83344235),并严格按照[issue模板](https://github.com/xuexiangjys/XHttp2/issues/new/choose)进行填写,节约大家的时间。 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍!!! 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍!!! 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍!!! ## 关于我 [![github](https://img.shields.io/badge/GitHub-xuexiangjys-blue.svg)](https://github.com/xuexiangjys) [![csdn](https://img.shields.io/badge/CSDN-xuexiangjys-green.svg)](http://blog.csdn.net/xuexiangjys) [![简书](https://img.shields.io/badge/简书-xuexiangjys-red.svg)](https://www.jianshu.com/u/6bf605575337) [![掘金](https://img.shields.io/badge/掘金-xuexiangjys-brightgreen.svg)](https://juejin.im/user/598feef55188257d592e56ed) [![知乎](https://img.shields.io/badge/知乎-xuexiangjys-violet.svg)](https://www.zhihu.com/people/xuexiangjys) ## X系列库快速集成 为了方便大家快速集成X系列框架库,我提供了一个空壳模版供大家参考使用: https://github.com/xuexiangjys/TemplateAppProject --- ## 特征 * 支持默认、全局、局部三个层次的配置功能。 * 支持动态配置和自定义底层框架OkHttpClient、Retrofit. * 加入基础ApiService,减少Api冗余。 * 支持多种方式访问网络GET、POST、PUT、DELETE等请求协议。 * 支持网络缓存,六种缓存策略可选,涵盖大多数业务场景。 * 支持固定添加header和动态添加header。 * 支持添加全局参数和动态添加局部参数。 * 支持文件下载、多文件上传和表单提交数据。 * 支持文件请求、上传、下载的进度回调、错误回调,也可以自定义回调。 * 支持任意数据结构的自动解析。 * 支持添加动态参数例如timeStamp时间戳、token、签名sign。 * 支持自定义的扩展API。 * 支持多个请求合并。 * 支持Cookie管理。 * 支持异步、同步请求。 * 支持Https、自签名网站Https的访问、双向验证。 * 支持失败重试机制,可以指定重试次数、重试间隔时间。 * 支持根据key删除网络缓存和清空网络缓存。 * 提供默认的标准ApiResult(遵循OpenApi格式)解析和回调,并且可自定义ApiResult。 * 支持取消数据请求,取消订阅,带有对话框的请求不需要手动取消请求,对话框消失会自动取消请求。 * 支持请求数据结果采用回调和订阅两种方式。 * 提供"默认API"、"接口协议"以及"统一请求实体"三种方式进行网络请求,支持自定义网络请求协议。 * 返回结果和异常统一处理,支持自定义异常处理。 * 结合RxJava,线程切换灵活。 * 请求实体支持注解配置,配置网络请求接口的url、是否需要验证token以及请求参数的key。 * 拥有统一的网络请求取消机制。 点击查看[项目设计类图](https://github.com/xuexiangjys/XHttp2/blob/master/img/xhttp_uml.png)。 --- ## 1、演示(请star支持) ### 1.1、Demo演示动画 ![][demo-gif] ### 1.2、Demo下载 [![downloads][download-svg]][download-url] ![][download-img] ### 1.3、api服务安装 服务端的搭建详细[请点击查看](https://github.com/xuexiangjys/XHttpApi) ## 2、如何使用 目前支持主流开发工具AndroidStudio的使用,直接配置build.gradle,增加依赖即可. ### 2.1、Android Studio导入方法,添加Gradle依赖 1.先在项目根目录的 build.gradle 的 repositories 添加: ``` allprojects { repositories { ... maven { url "https://jitpack.io" } } } ``` 2.然后在dependencies添加: 以下是版本说明,选择一个即可。 * androidx版本:2.0.0及以上 ``` dependencies { ... implementation 'com.github.xuexiangjys:XHttp2:2.0.1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'io.reactivex.rxjava2:rxjava:2.2.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' } ``` * support版本:1.0.4及以下 ``` dependencies { ... implementation 'com.github.xuexiangjys:XHttp2:1.0.4' implementation 'com.google.code.gson:gson:2.8.2' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'io.reactivex.rxjava2:rxjava:2.1.12' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' } ``` 3.在Application中初始化XHttpSDK ``` XHttpSDK.init(this); //初始化网络请求框架,必须首先执行 XHttpSDK.debug("XHttp"); //需要调试的时候执行 XHttpSDK.setBaseUrl(SettingSPUtils.getInstance().getApiURL()); //设置网络请求的基础地址 ``` 4.全局初始化配置(非必要) 除了上述的操作以外,你还可以使用`XHttp.getInstance()`对网络请求框架进行全局性参数配置,配置一些公用默认的参数,这样我们就不需要为每个请求都进行设置。方法如下: 方法名 | 备注 :-|:- debug | 设置日志的打印模式 setBaseUrl | 设置全局baseUrl setSubUrl | 设置全局subUrl setReadTimeOut | 设置全局读取超时时间 setWriteTimeOut | 设置全局写入超时时间 setConnectTimeout | 设置全局连接超时时间 setTimeout | 设置全局超时时间 setRetryCount | 设置全局超时重试次数 setRetryDelay | 设置全局超时重试延迟时间 setRetryIncreaseDelay | 设置全局超时重试延迟叠加时间 setCacheMode | 设置全局的缓存模式 setIsDiskCache | 设置是否是磁盘缓存 setMemoryMaxSize | 设置内存缓存的最大数量 setCacheTime | 设置全局的缓存过期时间 setCacheMaxSize | 设置全局的磁盘缓存大小,默认50M setCacheDirectory | 设置全局缓存的路径,默认是应用包下面的缓存 setCacheDiskConverter | 设置全局缓存的转换器 addCommonParams | 添加全局公共请求参数 addCommonHeaders | 添加全局公共请求参数 addInterceptor | 添加全局拦截器 addNetworkInterceptor | 添加全局网络拦截器 setOkproxy | 全局设置OkHttpClient的代理 setOkconnectionPool | 设置全局OkHttpClient的请求连接池 setOkclient | 全局为Retrofit设置自定义的OkHttpClient addConverterFactory | 设置全局Converter.Factory,默认GsonConverterFactory.create() addCallAdapterFactory | 设置全局CallAdapter.Factory,默认RxJavaCallAdapterFactory.create() setHostnameVerifier | 设置https的全局访问规则 setCertificates | 设置https的全局自签名证书 setCookieStore | 设置全局cookie存取规则 setStrictMode | 设置严格模式,在严格模式下,json返回的data数据不能为null --- ## 如何进行网络请求 需要注意的是,所以请求返回的结果必须要满足以下格式: ``` { "Code":0, //响应码,0为成功,否则失败 "Msg":"", //请求失败的原因说明 "Data":{} //返回的数据对象 } ``` 其中`Code`、`Msg`、`Data`建议使用大写字母,当然使用小写字母也没有问题,否则无法解析成功。 需要自定义返回的实体API请[点击查看](#自定义api请求) ### 1、使用XHttp默认api进行请求 1.使用XHttp.post、XHttp.get、XHttp.delete、XHttp.put、XHttp.downLoad构建请求。 2.修改request的请求参数。 方法名 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- baseUrl | String | / | 设置该请求的baseUrl timeOut | long | 15000 | 设置超时时间 accessToken | boolean | false | 是否需要验证token threadType | String | / | 设置请求的线程调度类型 syncRequest | boolean | false | 设置是否是同步请求(不开子线程) onMainThread | boolean | true | 请求完成后是否回到主线程 upJson | String | "" | 上传Json格式的数据请求 keepJson | boolean | false | 返回保持json的形式 retryCount | int | / | 设置超时重试的次数 retryDelay | int | / | 设置超时重试的延迟时间 retryIncreaseDelay | int | / | 设置超时重试叠加延时 headers | HttpHeaders | / | 添加头信息 params | HttpParams | /| 设置表单请求参数 cacheMode | CacheMode | CacheMode.NO_CACHE | 设置缓存的模式 3.调用`execute`方法执行请求。execute一般有如下两种方式: * execute(CallBack callBack): 直接回调结果。 * execute(Class clazz)和execute(Type type): 回调Observable对象,可通过订阅获取到结果。 4.请求使用演示 ``` XHttp.get("/user/getAllUser") .syncRequest(false) //异步请求 .onMainThread(true) //回到主线程 .execute(new SimpleCallBack>() { @Override public void onSuccess(List response) { refreshLayout.finishRefresh(true); if (response != null && response.size() > 0) { mUserAdapter.refresh(response); mLlStateful.showContent(); } else { mLlStateful.showEmpty(); } } @Override public void onError(ApiException e) { refreshLayout.finishRefresh(false); mLlStateful.showError(e.getMessage(), null); } }); ``` ``` XHttp.post("/user/deleteUser") .params("userId", item.getUserId()) .execute(Boolean.class) .subscribeWith(new TipRequestSubscriber() { @Override protected void onSuccess(Boolean aBoolean) { ToastUtils.toast("删除成功!"); setFragmentResult(RESULT_OK, null); popToBack(); } }); ``` --- ### 2、使用XHttpRequest封装的统一请求实体进行请求【仅支持post请求】 在使用它之前,需要下载/定义对应的实体协议,如下: ``` @RequestParams(url = "/user/addUser", accessToken = false) public static class UserService_AddUser extends XHttpRequest { /** * */ public User request; @Override protected Boolean getResponseEntityType() { return null; } } ``` 1.注解说明 * @RequestParams 注解参数 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- baseUrl | String | "" | 设置该请求的baseUrl url | String | "" | 请求网络接口地址 timeout | long | 15000 | 设置超时时间 accessToken | boolean | true | 设置是否需要验证token cacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式 * @ParamKey 注解参数 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- key | String | / | 请求参数的key 2.使用XHttpSDK进行请求。 * post(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread): 获取PostRequest请求(使用实体参数名作为请求Key)。 * postToMain(XHttpRequest xHttpRequest): 获取PostRequest请求(主线程->主线程)。 * postToIO(XHttpRequest xHttpRequest): 获取PostRequest请求(主线程->子线程)。 * postInThread(XHttpRequest xHttpRequest): 获取PostRequest请求(子线程->子线程)。 * execute(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread) : 执行PostRequest请求,返回observable对象(使用实体参数名作为请求Key)。 * `executeToMain(XHttpRequest xHttpRequest)`: 执行post请求,返回observable对象(主线程->主线程) * `executeToMain(XHttpRequest xHttpRequest,BaseSubscriber subscriber)`: 执行post请求并进行订阅,返回订阅信息(主线程->主线程) 3.请求使用演示。 ``` XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser()); XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber(mIProgressLoader) { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("用户添加成功!"); mRefreshLayout.autoRefresh(); } }); ``` --- ### 3、使用XHttpProxy代理进行请求 在使用它之前,需要下载/定义对应的接口协议,如下: ``` /** * 图书管理 */ public interface IBook { /** * 购买书 * * @param bookId 图书ID * @param userId 用户ID * @param number 购买数量 */ @NetMethod(parameterNames = {"bookId", "userId", "number"}, url = "/order/addOrder/", accessToken = false) Observable buyBook(int bookId, int userId, int number); /** * 获取图书 * * @param pageNum 第几页数 * @param pageSize 每页的数量 */ @NetMethod(parameterNames = {"pageNum", "pageSize"}, paramType = FORM_BODY, url = "/book/findBooks/", accessToken = false) Observable> getBooks(int pageNum, int pageSize); /** * 获取所有图书 * */ @NetMethod(action = GET, url = "/book/getAllBook", accessToken = false) Observable> getAllBooks(); } ``` 1.注解说明 * @NetMethod 注解参数 | 类型 | 默认值 | 备注 :-|:-:|:-:|:- parameterNames | String\[\] | {} | 参数名集合 paramType | int | JSON=1 | param的类型 action | String | POST="post" | 请求动作 baseUrl | String | "" | 设置该请求的baseUrl url | String | "" | 请求网络接口地址 timeout | long | 15000 | 设置超时时间 keepJson | boolean | false | 是否保存json accessToken | boolean | true | 设置是否需要验证token cacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式 2.使用XHttpProxy进行请求。 构建一个XHttpProxy,将定义的api接口传入后,直接调用接口进行请求。 构造XHttpProxy可以传入`ThreadType`,默认是`ThreadType.TO_MAIN`。 * TO_MAIN: executeToMain(main -> io -> main) > 【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程),且响应的线程又切换至主线程】 * TO_IO: executeToIO(main -> io -> io) > 【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程),不过响应的线程不变,还是之前请求的那个io线程】 * IN_THREAD: executeInThread(io -> io -> io) > 【注意】请确保网络请求在子线程中才可以使用该类型【实质是不做任何线程调度的同步请求】 3.请求使用演示。 ``` //使用XHttpProxy进行接口代理请求 XHttpProxy.proxy(TestApi.IOrder.class) .buyBook(mBookAdapter.getItem(position).getBookId(), UserManager.getInstance().getUser().getUserId(), 1) .subscribeWith(new TipRequestSubscriber() { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("图书购买" + (aBoolean ? "成功" : "失败") + "!"); mRefreshLayout.autoRefresh(); } }); ``` --- ### 4、文件上传和下载 1.文件上传【multipart/form-data】 使用post的文件表单上传。使用`XHttp.post`,然后使用`params`传递附带的参数,使用`uploadFile`传递需要上传的文件,使用示例如下: ``` mIProgressLoader.updateMessage("上传中..."); XHttp.post("/book/uploadBookPicture") .params("bookId", book.getBookId()) .uploadFile("file", FileUtils.getFileByPath(mPicturePath), new IProgressResponseCallBack() { @Override public void onResponseProgress(long bytesWritten, long contentLength, boolean done) { } }).execute(Boolean.class) .compose(RxLifecycle.with(this).bindToLifecycle()) .subscribeWith(new ProgressLoadingSubscriber(mIProgressLoader) { @Override public void onSuccess(Boolean aBoolean) { mIsEditSuccess = true; ToastUtils.toast("图片上传" + (aBoolean ? "成功" : "失败") + "!"); } }); ``` 2.文件下载 使用`XHttp.downLoad`,传入下载的地址url、保存文件的路径以及文件名即可完成文件的下载,使用示例如下: ``` XHttp.downLoad(BookAdapter.getBookImgUrl(book)) .savePath(PathUtils.getExtPicturesPath()) .execute(new DownloadProgressCallBack() { @Override public void onStart() { HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "图片下载中...", true); } @Override public void onError(ApiException e) { ToastUtils.toast(e.getMessage()); HProgressDialogUtils.cancel(); } @Override public void update(long bytesRead, long contentLength, boolean done) { HProgressDialogUtils.onLoading(contentLength, bytesRead); //更新进度条 } @Override public void onComplete(String path) { ToastUtils.toast("图片下载成功, 保存路径:" + path); HProgressDialogUtils.cancel(); } }); ``` --- ## 高阶网络请求操作 ### 请求生命周期绑定 1.请求loading加载和请求生命周期绑定 在请求时,订阅`ProgressLoadingSubscriber`或者`ProgressLoadingCallBack`,传入请求消息加载者`IProgressLoader`,即可完成生命周期的绑定。示例如下: ``` XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser()); XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber(mIProgressLoader) { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("用户添加成功!"); mRefreshLayout.autoRefresh(); } }); ``` 2.网络请求生命周期和Activity/Fragment生命周期绑定 (1)这里需要依赖一下RxUtil2 ``` implementation 'com.github.xuexiangjys:rxutil2:1.1.2' ``` (2)在所在的Activity的onCreate()下锁定Activity. ``` @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RxLifecycle.injectRxLifecycle(this); } ``` (3)然后在请求中使用RxJava的`compose`的操作符进行绑定。 ``` .compose(RxLifecycle.with(this).bindToLifecycle()) ``` ### 拦截器 #### 日志拦截器 (1)框架默认提供一个实现好的日志拦截器`HttpLoggingInterceptor`,通过`XHttpSDK.debug("XHttp");`就可以设置进去,它有5种打印模式 * NONE: 不打印log * BASIC: 只打印"请求首行"和"响应首行"。 * HEADERS: 打印请求和响应的所有 Header * PARAM: 只打印请求和响应参数 * BODY: 打印所有数据(默认是这种) (2)如果需要对网络请求的相关参数进行自定义记录的话,可以继承`HttpLoggingInterceptor`实现一个自己的网络请求日志拦截器,重写`logForRequest`和`logForResponse`两个方法即可。 (3)设置自定义的日志拦截器. ``` XHttpSDK.debug(new CustomLoggingInterceptor()); ``` #### 动态参数添加拦截器 > 有时候,我们需要对所有请求添加一些固定的请求参数,但是这些参数的值又是变化的,这个时候我们就需要动态添加请求参数【例如,请求的token、时间戳以及签名等】 (1)继承`BaseDynamicInterceptor`,实现`updateDynamicParams`方法,如下: ``` @Override protected TreeMap updateDynamicParams(TreeMap dynamicMap) { if (isAccessToken()) {//是否添加token dynamicMap.put("token", TokenManager.getInstance().getToken()); } if (isSign()) {//是否添加签名 dynamicMap.put("sign", TokenManager.getInstance().getSign()); } if (isTimeStamp()) {//是否添加请求时间戳 dynamicMap.put("timeStamp", DateUtils.getNowMills()); } return dynamicMap;//dynamicMap:是原有的全局参数+局部参数+新增的动态参数 } ``` (2)设置动态参数添加拦截器。 ``` XHttpSDK.addInterceptor(new CustomDynamicInterceptor()); //设置动态参数添加拦截器 ``` #### 失效请求校验拦截器 > 当服务端返回一些独特的错误码(一般是token校验错误、失效,请求过于频繁等),需要我们进行全局性的拦截捕获,并作出相应的响应时,我们就需要定义一个特殊的拦截器求处理这些请求。 (1)继承`BaseExpiredInterceptor`,实现`isResponseExpired`和`responseExpired`方法,如下: ``` /** * 判断是否是失效的响应 * * @param oldResponse * @param bodyString * @return {@code true} : 失效
{@code false} : 有效 */ @Override protected ExpiredInfo isResponseExpired(Response oldResponse, String bodyString) { int code = JSONUtils.getInt(bodyString, ApiResult.CODE, 0); ExpiredInfo expiredInfo = new ExpiredInfo(code); switch (code) { case TOKEN_INVALID: case TOKEN_MISSING: expiredInfo.setExpiredType(KEY_TOKEN_EXPIRED) .setBodyString(bodyString); break; case AUTH_ERROR: expiredInfo.setExpiredType(KEY_UNREGISTERED_USER) .setBodyString(bodyString); break; default: break; } return expiredInfo; } /** * 失效响应的处理 * * @return 获取新的有效请求响应 */ @Override protected Response responseExpired(Response oldResponse, Chain chain, ExpiredInfo expiredInfo) { switch(expiredInfo.getExpiredType()) { case KEY_TOKEN_EXPIRED: User user = TokenManager.getInstance().getLoginUser(); if (user != null) { final boolean[] isGetNewToken = {false}; HttpLog.e("正在重新获取token..."); XHttpProxy.proxy(TestApi.IAuthorization.class, ThreadType.IN_THREAD) .login(user.getLoginName(), user.getPassword()) .subscribeWith(new NoTipRequestSubscriber() { @Override protected void onSuccess(LoginInfo loginInfo) { TokenManager.getInstance() .setToken(loginInfo.getToken()) .setLoginUser(loginInfo.getUser()); isGetNewToken[0] = true; HttpLog.e("重新获取token成功:" + loginInfo.getToken()); } }); if (isGetNewToken[0]) { try { HttpLog.e("使用新的token重新进行请求..."); return chain.proceed(HttpUtils.updateUrlParams(chain.request(), "token", TokenManager.getInstance().getToken())); } catch (IOException e) { e.printStackTrace(); } } } else { XRouter.getInstance().build("/xhttp/login").navigation(); return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), "请先进行登录!"); } break; case KEY_UNREGISTERED_USER: return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), "非法用户登录!"); default: break; } return null; } ``` (2)设置失效请求校验拦截器。 ``` XHttpSDK.addInterceptor(new CustomExpiredInterceptor()); //请求失效校验拦截器 ``` ### 自定义API请求 #### 自定义请求响应的API结构 如果你不想使用默认的ApiResult实体作为统一的服务端响应实体,比如说你想要下面的响应实体: ``` private int errorCode; //请求的错误码 private String errorInfo; //请求错误的原因描述 private T result; //请求的结果 private long timeStamp; //服务端返回的时间戳 ``` (1)首先,继承`ApiResult`实体,重写其`getCode`、`getMsg`、`isSuccess`和`getData`方法。 ``` public class CustomApiResult extends ApiResult { private int errorCode; private String errorInfo; private T result; private long timeStamp; public int getErrorCode() { return errorCode; } public CustomApiResult setErrorCode(int errorCode) { this.errorCode = errorCode; return this; } public String getErrorInfo() { return errorInfo; } public CustomApiResult setErrorInfo(String errorInfo) { this.errorInfo = errorInfo; return this; } public T getResult() { return result; } public CustomApiResult setResult(T result) { this.result = result; return this; } public long getTimeStamp() { return timeStamp; } public CustomApiResult setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; return this; } @Override public int getCode() { return errorCode; } @Override public String getMsg() { return errorInfo; } @Override public boolean isSuccess() { return errorCode == 0; } @Override public T getData() { return result; } @Override public String toString() { return "ApiResult{" + "errorCode='" + errorCode + '\'' + ", errorInfo='" + errorInfo + '\'' + ", timeStamp='" + timeStamp + '\'' + ", result=" + result + '}'; } } ``` (2)进行请求的时候使用`execute(CallBackProxy)`或者`execute(CallClazzProxy`方法进行请求 ``` XHttp.get("/test/testCustomResult") .execute(new CallBackProxy, Boolean>(new TipRequestCallBack() { @Override public void onSuccess(Boolean response) throws Throwable { ToastUtils.toast("请求成功:" + response); } }){}); //千万注意,这里的{}一定不能去掉,否则解析错误 ``` 【注意】上面提示的{}一定不能去掉,否则解析错误, 会报"ApiResult.class.isAssignableFrom(cls) err!!"的错误。 如果你觉得写一长串比较麻烦,你可以自定义请求继承你需要的请求方式,例如这里是get请求,我们可以这样写: ``` public class CustomGetRequest extends GetRequest { public CustomGetRequest(String url) { super(url); } @Override public Observable execute(Type type) { return execute(new CallClazzProxy, T>(type) { }); } @Override public Disposable execute(CallBack callBack) { return execute(new CallBackProxy, T>(callBack) { }); } } ``` 然后我们就可以用自定义的`CustomGetRequest`进行请求了,是不是简化了很多呢。 ``` new CustomGetRequest("/test/testCustomResult") .execute(new TipRequestCallBack() { @Override public void onSuccess(Boolean response) throws Throwable { ToastUtils.toast("请求成功:" + response); } }); ``` #### 使用自定义的retrofit接口 如果你对retrofit接口情有独钟,我也提供了相应的api方便调用. 1.定义retrofit接口。例如我定义一个用户添加的接口: ``` /** * 使用的是retrofit的接口定义 */ public interface UserService { @POST("/user/registerUser/") @Headers({"Content-Type: application/json", "Accept: application/json"}) Observable> registerUser(@Body RequestBody jsonBody); @POST("/user/registerUser/") @Headers({"Content-Type: application/json", "Accept: application/json"}) Observable register(@Body User user); } ``` 2.使用`XHttp.custom()`构建的`CustomRequest`进行请求,你可以使用`apiCall`和`call`进行请求。 * apiCall: 针对的是retrofit定义的接口,返回的是Observable>的情况,对ApiResult进行拆包,直接获取数据。对于上面定义的第一个接口`registerUser`。 * call: 针对的是retrofit定义的接口,返回的是Observable的情况,不对ApiResult进行拆包。对于上面定义的第二个接口`register`。 使用示例如下: ``` CustomRequest request = XHttp.custom(); request.apiCall(request.create(TestApi.UserService.class) .registerUser(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser()))) .subscribeWith(new TipRequestSubscriber() { @Override protected void onSuccess(Boolean aBoolean) { ToastUtils.toast("添加用户成功!"); } }); ``` ``` CustomRequest request = XHttp.custom(); request.call(request.create(TestApi.UserService.class) .register(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser()))) .subscribeWith(new TipRequestSubscriber() { @Override protected void onSuccess(ApiResult apiResult) { ToastUtils.toast("添加用户成功!"); showResult(JsonUtil.toJson(apiResult)); } }); ``` ### 缓存策略 目前框架提供了如下8种缓存策略: * NO_CACHE: 不使用缓存(默认方式) * DEFAULT: 完全按照HTTP协议的默认缓存规则,走OKhttp的Cache缓存 * FIRST_REMOTE: 先请求网络,请求网络失败后再加载缓存 * FIRST_CACHE: 先加载缓存,缓存没有再去请求网络 * ONLY_REMOTE: 仅加载网络,但数据依然会被缓存 * ONLY_CACHE: 只读取缓存 * CACHE_REMOTE: 先使用缓存,不管是否存在,仍然请求网络,会回调两次 * CACHE_REMOTE_DISTINCT: 先使用缓存,不管是否存在,仍然请求网络,会先把缓存回调给你,等网络请求回来发现数据是一样的就不会再返回,否则再返回(这样做的目的是防止数据是一样的你也需要刷新界面) 对于缓存的实现,提供了磁盘缓存`LruDiskCache`和内存缓存`LruMemoryCache`两种实现,默认使用的是磁盘缓存。 (1)可以先进行缓存的全局性配置,配置缓存的有效期、缓存大小,缓存路径、序列化器等。 ``` XHttp.getInstance() .setIsDiskCache(true) //设置使用磁盘缓存 .setCacheTime(60 * 1000) //设置全局缓存有效期为一分钟 .setCacheVersion(1) //设置全局缓存的版本 .setCacheDirectory(Utils.getDiskCacheDir(this, "XHttp")) //设置全局缓存保存的目录路径 .setCacheMode(CacheMode.NO_CACHE) //设置全局的缓存策略 .setCacheDiskConverter(new GsonDiskConverter())//默认缓存使用序列化转化 .setCacheMaxSize(50 * 1024 * 1024);//设置缓存大小为50M ``` (2)在进行请求的时候,设置缓存模式和缓存的key即可。如下: ``` XHttp.get("/book/getAllBook") .timeOut(10 * 1000)//测试局部超时10s .cacheMode(mCacheMode) .cacheKey(CACHE_KEY)//缓存key .retryCount(5)//重试次数 .cacheTime(5 * 60)//缓存时间300s,默认-1永久缓存 okhttp和自定义缓存都起作用 .cacheDiskConverter(new GsonDiskConverter())//默认使用的是 new SerializableDiskConverter(); .timeStamp(true) .execute(new ProgressLoadingCallBack>>(mIProgressLoader) { @Override public void onSuccess(CacheResult> cacheResult) { ToastUtils.toast("请求成功!"); String from; if (cacheResult.isFromCache) { from = "我来自缓存"; } else { from = "我来自远程网络"; } showResult(from + "\n" + JsonUtil.toJson(cacheResult.data)); } @Override public void onError(ApiException e) { super.onError(e); ToastUtils.toast(e.getDisplayMessage()); } }); ``` -------------- ## 混淆配置 ``` #XHttp2 -keep class com.xuexiang.xhttp2.model.** { *; } -keep class com.xuexiang.xhttp2.cache.model.** { *; } -keep class com.xuexiang.xhttp2.cache.stategy.**{*;} -keep class com.xuexiang.xhttp2.annotation.** { *; } #okhttp -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { *;} -dontwarn okio.** -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault -dontwarn javax.annotation.** # Retrofit -dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Exceptions # RxJava RxAndroid -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } #如果用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错 -keepattributes Signature -keep class com.google.gson.stream.** { *; } -keepattributes EnclosingMethod -keep class org.xz_sale.entity.**{*;} -keep class com.google.gson.** {*;} -keep class com.google.**{*;} -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.stream.** { *; } -keep class com.google.gson.examples.android.model.** { *; } ``` --- ## 特别感谢 https://github.com/zhou-you/RxEasyHttp ## 如果觉得项目还不错,可以考虑打赏一波 > 你的打赏是我维护的动力,我将会列出所有打赏人员的清单在下方作为凭证,打赏前请留下打赏项目的备注! ![pay.png](https://img.rruu.net/image/5f871d00045da) 感谢下面小伙伴的打赏: 姓名 | 金额 | 方式 :-|:-|:- *声 | 50¥ | 微信 **东 | 5¥ | 支付宝 ## 联系方式 [![](https://img.shields.io/badge/点击一键加入QQ交流群-602082750-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=9922861ef85c19f1575aecea0e8680f60d9386080a97ed310c971ae074998887) > 更多资讯内容,欢迎扫描关注我的个人微信公众号:【我的Android开源之旅】 ![gzh_weixin.jpg](https://img.rruu.net/image/5f871cfff3194) [demo-gif]: https://img.rruu.net/image/5f7dc798ea8d9 [download-svg]: https://img.shields.io/badge/downloads-2.61M-blue.svg [download-url]: https://github.com/xuexiangjys/XHttp2/blob/master/apk/xhttp2_demo_1.0.apk?raw=true [download-img]: https://img.rruu.net/image/5f7dc792910c3