{"id":19765687,"url":"https://github.com/xuexiangjys/xhttp2","last_synced_at":"2025-04-05T18:07:39.983Z","repository":{"id":46590407,"uuid":"135068737","full_name":"xuexiangjys/XHttp2","owner":"xuexiangjys","description":"💪A powerful network request library, encapsulated using the RxJava2 + Retrofit2 + OKHttp combination.(一个功能强悍的网络请求库，使用RxJava2 + Retrofit2 + OKHttp组合进行封装)","archived":false,"fork":false,"pushed_at":"2023-12-15T14:38:55.000Z","size":14279,"stargazers_count":408,"open_issues_count":3,"forks_count":86,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-29T17:07:46.658Z","etag":null,"topics":["cookie","gradle","rxjava","rxjava2-retrofit2-okhttp","x-library","xhttp"],"latest_commit_sha":null,"homepage":"https://github.com/xuexiangjys/XHttp2/wiki","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xuexiangjys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"custom":"https://github.com/xuexiangjys/Resource/blob/master/doc/sponsor.md"}},"created_at":"2018-05-27T17:47:56.000Z","updated_at":"2025-01-22T08:56:09.000Z","dependencies_parsed_at":"2024-01-19T16:08:54.438Z","dependency_job_id":"97adbd7f-77c6-415c-a2ed-357e71c60ff6","html_url":"https://github.com/xuexiangjys/XHttp2","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xuexiangjys%2FXHttp2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xuexiangjys%2FXHttp2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xuexiangjys%2FXHttp2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xuexiangjys%2FXHttp2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xuexiangjys","download_url":"https://codeload.github.com/xuexiangjys/XHttp2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378142,"owners_count":20929296,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cookie","gradle","rxjava","rxjava2-retrofit2-okhttp","x-library","xhttp"],"created_at":"2024-11-12T04:19:07.970Z","updated_at":"2025-04-05T18:07:39.965Z","avatar_url":"https://github.com/xuexiangjys.png","language":"Java","funding_links":["https://github.com/xuexiangjys/Resource/blob/master/doc/sponsor.md"],"categories":[],"sub_categories":[],"readme":"# XHttp2\n\n[![](https://jitpack.io/v/xuexiangjys/XHttp2.svg)](https://jitpack.io/#xuexiangjys/XHttp2)\n[![api](https://img.shields.io/badge/API-14+-brightgreen.svg)](https://android-arsenal.com/api?level=14)\n[![I](https://img.shields.io/github/issues/xuexiangjys/XHttp2.svg)](https://github.com/xuexiangjys/XHttp2/issues)\n[![Star](https://img.shields.io/github/stars/xuexiangjys/XHttp2.svg)](https://github.com/xuexiangjys/XHttp2)\n\n一个功能强悍的网络请求库，使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki)，体验一下吧！\n\n在提issue前，请先阅读[【提问的智慧】](https://xuexiangjys.blog.csdn.net/article/details/83344235)，并严格按照[issue模板](https://github.com/xuexiangjys/XHttp2/issues/new/choose)进行填写，节约大家的时间。\n\n在使用前，请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍！！！\n\n在使用前，请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍！！！\n\n在使用前，请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XHttp2/wiki),重要的事情说三遍！！！\n\n## 关于我\n\n| 公众号   | 掘金     |  知乎    |  CSDN   |   简书   |   思否  |   哔哩哔哩  |   今日头条\n|---------|---------|--------- |---------|---------|---------|---------|---------|\n| [我的Android开源之旅](https://t.1yb.co/Irse)  |  [点我](https://juejin.im/user/598feef55188257d592e56ed/posts)    |   [点我](https://www.zhihu.com/people/xuexiangjys/posts)       |   [点我](https://xuexiangjys.blog.csdn.net/)  |   [点我](https://www.jianshu.com/u/6bf605575337)  |   [点我](https://segmentfault.com/u/xuexiangjys)  |   [点我](https://space.bilibili.com/483850585)  |   [点我](https://img.rruu.net/image/5ff34ff7b02dd)\n\n## X系列库快速集成\n\n为了方便大家快速集成X系列框架库，我提供了一个空壳模版供大家参考使用: https://github.com/xuexiangjys/TemplateAppProject\n\n---\n\n## 特征\n\n* 支持默认、全局、局部三个层次的配置功能。\n* 支持动态配置和自定义底层框架OkHttpClient、Retrofit.\n* 加入基础ApiService，减少Api冗余。\n* 支持多种方式访问网络GET、POST、PUT、DELETE等请求协议。\n* 支持网络缓存,六种缓存策略可选,涵盖大多数业务场景。\n* 支持固定添加header和动态添加header。\n* 支持添加全局参数和动态添加局部参数。\n* 支持文件下载、多文件上传和表单提交数据。\n* 支持文件请求、上传、下载的进度回调、错误回调，也可以自定义回调。\n* 支持任意数据结构的自动解析。\n* 支持添加动态参数例如timeStamp时间戳、token、签名sign。\n* 支持自定义的扩展API。\n* 支持多个请求合并。\n* 支持Cookie管理。\n* 支持异步、同步请求。\n* 支持Https、自签名网站Https的访问、双向验证。\n* 支持失败重试机制，可以指定重试次数、重试间隔时间。\n* 支持根据key删除网络缓存和清空网络缓存。\n* 提供默认的标准ApiResult（遵循OpenApi格式）解析和回调，并且可自定义ApiResult。\n* 支持取消数据请求，取消订阅，带有对话框的请求不需要手动取消请求，对话框消失会自动取消请求。\n* 支持请求数据结果采用回调和订阅两种方式。\n* 提供\"默认API\"、\"接口协议\"以及\"统一请求实体\"三种方式进行网络请求，支持自定义网络请求协议。\n* 返回结果和异常统一处理，支持自定义异常处理。\n* 结合RxJava，线程切换灵活。\n* 请求实体支持注解配置，配置网络请求接口的url、是否需要验证token以及请求参数的key。\n* 拥有统一的网络请求取消机制。\n\n点击查看[项目设计类图](https://github.com/xuexiangjys/XHttp2/blob/master/img/xhttp_uml.png)。\n\n## Star趋势图\n\n[![Stargazers over time](https://starchart.cc/xuexiangjys/XHttp2.svg)](https://starchart.cc/xuexiangjys/XHttp2)\n\n---\n\n## 1、演示（请star支持）\n\n### 1.1、Demo演示动画\n\n![][demo-gif]\n\n### 1.2、Demo下载\n\n[![downloads][download-svg]][download-url]\n\n![][download-img]\n\n### 1.3、api服务安装\n\n服务端的搭建详细[请点击查看](https://github.com/xuexiangjys/XHttpApi)\n\n## 2、如何使用\n目前支持主流开发工具AndroidStudio的使用，直接配置build.gradle，增加依赖即可.\n\n### 2.1、Android Studio导入方法，添加Gradle依赖\n\n1.先在项目根目录的 build.gradle 的 repositories 添加:\n```\nallprojects {\n     repositories {\n        ...\n        maven { url \"https://jitpack.io\" }\n    }\n}\n```\n\n2.然后在应用项目(一般是app)的 `build.gradle` 的 dependencies 添加:\n\n以下是版本说明，选择一个即可。\n\n* androidx版本：2.0.0及以上\n\n```\ndependencies {\n  ...\n  implementation 'com.github.xuexiangjys:XHttp2:2.0.4'\n\n  implementation 'com.google.code.gson:gson:2.8.5'\n  implementation 'com.squareup.okhttp3:okhttp:3.10.0'\n  implementation 'io.reactivex.rxjava2:rxjava:2.2.0'\n  implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'\n}\n```\n\n* support版本：1.0.4及以下\n\n```\ndependencies {\n  ...\n  implementation 'com.github.xuexiangjys:XHttp2:1.0.4'\n\n  implementation 'com.google.code.gson:gson:2.8.2'\n  implementation 'com.squareup.okhttp3:okhttp:3.10.0'\n  implementation 'io.reactivex.rxjava2:rxjava:2.1.12'\n  implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'\n}\n```\n\n\n\n3.在Application中初始化XHttpSDK\n\n```\nXHttpSDK.init(this);   //初始化网络请求框架，必须首先执行\nXHttpSDK.debug(\"XHttp\");  //需要调试的时候执行\nXHttpSDK.setBaseUrl(SettingSPUtils.getInstance().getApiURL());  //设置网络请求的基础地址\n```\n\n4.全局初始化配置（非必要)\n\n除了上述的操作以外，你还可以使用`XHttp.getInstance()`对网络请求框架进行全局性参数配置，配置一些公用默认的参数，这样我们就不需要为每个请求都进行设置。方法如下：\n\n方法名 | 备注\n:-|:-\ndebug | 设置日志的打印模式\nsetBaseUrl | 设置全局baseUrl\nsetSubUrl | 设置全局subUrl\nsetReadTimeOut | 设置全局读取超时时间\nsetWriteTimeOut | 设置全局写入超时时间\nsetConnectTimeout | 设置全局连接超时时间\nsetTimeout | 设置全局超时时间\nsetRetryCount | 设置全局超时重试次数\nsetRetryDelay | 设置全局超时重试延迟时间\nsetRetryIncreaseDelay | 设置全局超时重试延迟叠加时间\nsetCacheMode | 设置全局的缓存模式\nsetIsDiskCache | 设置是否是磁盘缓存\nsetMemoryMaxSize | 设置内存缓存的最大数量\nsetCacheTime | 设置全局的缓存过期时间\nsetCacheMaxSize | 设置全局的磁盘缓存大小,默认50M\nsetCacheDirectory | 设置全局缓存的路径，默认是应用包下面的缓存\nsetCacheDiskConverter | 设置全局缓存的转换器\naddCommonParams | 添加全局公共请求参数\naddCommonHeaders | 添加全局公共请求参数\naddInterceptor | 添加全局拦截器\naddNetworkInterceptor | 添加全局网络拦截器\nsetOkproxy | 全局设置OkHttpClient的代理\nsetOkconnectionPool | 设置全局OkHttpClient的请求连接池\nsetOkclient | 全局为Retrofit设置自定义的OkHttpClient\naddConverterFactory | 设置全局Converter.Factory,默认GsonConverterFactory.create()\naddCallAdapterFactory | 设置全局CallAdapter.Factory,默认RxJavaCallAdapterFactory.create()\nsetHostnameVerifier | 设置https的全局访问规则\nsetCertificates | 设置https的全局自签名证书\nsetCookieStore | 设置全局cookie存取规则\nsetStrictMode | 设置严格模式，在严格模式下，json返回的data数据不能为null\n\n---\n\n## 如何进行网络请求\n\n需要注意的是，所以请求返回的结果必须要满足以下格式:\n\n```\n{\n    \"Code\":0, //响应码，0为成功，否则失败\n    \"Msg\":\"\", //请求失败的原因说明\n    \"Data\":{} //返回的数据对象\n}\n```\n\n其中`Code`、`Msg`、`Data`建议使用大写字母，当然使用小写字母也没有问题，否则无法解析成功。\n\n***【注意】这里`Code`为0才代表请求成功，如果你的Code码不是0代表成功，你可以使用`XHttpSDK.setSuccessCode`设置代表成功的值。***\n\n需要自定义返回的实体API请[点击查看](#自定义api请求)\n\n### 1、使用XHttp默认api进行请求\n\n1.使用XHttp.post、XHttp.get、XHttp.delete、XHttp.put、XHttp.downLoad构建请求。\n\n2.修改request的请求参数。\n\n方法名 | 类型 | 默认值 | 备注\n:-|:-:|:-:|:-\nbaseUrl | String | ／ | 设置该请求的baseUrl\ntimeOut | long | 15000 | 设置超时时间\naccessToken | boolean | false | 是否需要验证token\nthreadType | String | ／ | 设置请求的线程调度类型\nsyncRequest | boolean | false | 设置是否是同步请求（不开子线程）\nonMainThread | boolean | true | 请求完成后是否回到主线程\nupJson | String | \"\" | 上传Json格式的数据请求\nkeepJson | boolean | false | 返回保持json的形式\nretryCount | int | ／ | 设置超时重试的次数\nretryDelay | int | ／ | 设置超时重试的延迟时间\nretryIncreaseDelay | int | ／ | 设置超时重试叠加延时\nheaders | HttpHeaders | ／ | 添加头信息\nparams | HttpParams | ／| 设置表单请求参数\ncacheMode | CacheMode | CacheMode.NO_CACHE | 设置缓存的模式\n\n3.调用`execute`方法执行请求。execute一般有如下两种方式：\n\n* execute(CallBack callBack): 直接回调结果。\n\n* execute(Class clazz)和execute(Type type): 回调Observable\u003cT\u003e对象，可通过订阅获取到结果。\n\n4.请求使用演示\n\n```\nXHttp.get(\"/user/getAllUser\")\n        .syncRequest(false) //异步请求\n        .onMainThread(true) //回到主线程\n        .execute(new SimpleCallBack\u003cList\u003cUser\u003e\u003e() {\n            @Override\n            public void onSuccess(List\u003cUser\u003e response) {\n                refreshLayout.finishRefresh(true);\n                if (response != null \u0026\u0026 response.size() \u003e 0) {\n                    mUserAdapter.refresh(response);\n                    mLlStateful.showContent();\n                } else {\n                    mLlStateful.showEmpty();\n                }\n            }\n            @Override\n            public void onError(ApiException e) {\n                refreshLayout.finishRefresh(false);\n                mLlStateful.showError(e.getMessage(), null);\n            }\n\n        });\n```\n\n```\nXHttp.post(\"/user/deleteUser\")\n        .params(\"userId\", item.getUserId())\n        .execute(Boolean.class)\n        .subscribeWith(new TipRequestSubscriber\u003cBoolean\u003e() {\n            @Override\n            protected void onSuccess(Boolean aBoolean) {\n                ToastUtils.toast(\"删除成功！\");\n                setFragmentResult(RESULT_OK, null);\n                popToBack();\n            }\n        });\n\n```\n\n---\n\n### 2、使用XHttpRequest封装的统一请求实体进行请求【仅支持post请求】\n\n在使用它之前，需要下载/定义对应的实体协议，如下：\n\n```\n@RequestParams(url = \"/user/addUser\", accessToken = false)\npublic static class UserService_AddUser extends XHttpRequest {\n\n    /**\n     *\n     */\n    public User request;\n\n    @Override\n    protected Boolean getResponseEntityType() {\n        return null;\n    }\n}\n```\n\n1.注解说明\n\n* @RequestParams\n\n注解参数 | 类型 | 默认值 | 备注\n:-|:-:|:-:|:-\nbaseUrl | String | \"\" | 设置该请求的baseUrl\nurl | String | \"\" | 请求网络接口地址\ntimeout | long | 15000 | 设置超时时间\nkeepJson | boolean | false | 是否保存json\naccessToken | boolean | true | 设置是否需要验证token\ncacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式\ncacheTime | long | -2(使用全局设置） | 设置缓存有效时间\n\n* @ParamKey\n\n注解参数 | 类型 | 默认值 | 备注\n:-|:-:|:-:|:-\nkey | String | / | 请求参数的key\n\n2.使用XHttpSDK进行请求。\n\n* post(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread)：\n  获取PostRequest请求（使用实体参数名作为请求Key）。\n\n* postToMain(XHttpRequest xHttpRequest)：\n  获取PostRequest请求（主线程-\u003e主线程）。\n\n* postToIO(XHttpRequest xHttpRequest)：\n  获取PostRequest请求（主线程-\u003e子线程）。\n\n* postInThread(XHttpRequest xHttpRequest)：\n  获取PostRequest请求（子线程-\u003e子线程）。\n\n* execute(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread) ：\n  执行PostRequest请求，返回observable对象（使用实体参数名作为请求Key）。\n\n* `executeToMain(XHttpRequest xHttpRequest)`:\n  执行post请求，返回observable对象（主线程-\u003e主线程）\n\n* `executeToMain(XHttpRequest xHttpRequest，BaseSubscriber\u003cT\u003e subscriber)`:\n  执行post请求并进行订阅，返回订阅信息（主线程-\u003e主线程）\n\n3.请求使用演示。\n\n```\nXHttpRequest req = ApiProvider.getAddUserReq(getRandomUser());\nXHttpSDK.executeToMain(req, new ProgressLoadingSubscriber\u003cBoolean\u003e(mIProgressLoader) {\n    @Override\n    public void onSuccess(Boolean aBoolean) {\n        ToastUtils.toast(\"用户添加成功！\");\n        mRefreshLayout.autoRefresh();\n    }\n});\n```\n---\n\n### 3、使用XHttpProxy代理进行请求\n\n在使用它之前，需要下载/定义对应的接口协议，如下：\n\n```\n/**\n * 图书管理\n */\npublic interface IBook {\n    /**\n     * 购买书\n     *\n     * @param bookId 图书ID\n     * @param userId 用户ID\n     * @param number 购买数量\n     */\n    @NetMethod(parameterNames = {\"bookId\", \"userId\", \"number\"}, url = \"/order/addOrder/\", accessToken = false)\n    Observable\u003cBoolean\u003e buyBook(int bookId, int userId, int number);\n    /**\n     * 获取图书\n     *\n     * @param pageNum 第几页数\n     * @param pageSize 每页的数量\n     */\n    @NetMethod(parameterNames = {\"pageNum\", \"pageSize\"}, paramType = FORM_BODY, url = \"/book/findBooks/\", accessToken = false)\n    Observable\u003cList\u003cBook\u003e\u003e getBooks(int pageNum, int pageSize);\n\n    /**\n     * 获取所有图书\n     *\n     */\n    @NetMethod(action = GET, url = \"/book/getAllBook\", accessToken = false)\n    Observable\u003cList\u003cBook\u003e\u003e getAllBooks();\n}\n```\n\n1.注解说明\n\n* @NetMethod\n\n注解参数 | 类型 | 默认值 | 备注\n:-|:-:|:-:|:-\nparameterNames | String\\[\\] | {} | 参数名集合\nparamType | int | JSON=1 | param的类型\naction | String | POST=\"post\" | 请求动作\nbaseUrl | String | \"\" | 设置该请求的baseUrl\nurl | String | \"\" | 请求网络接口地址\ntimeout | long | 15000 | 设置超时时间\nkeepJson | boolean | false | 是否保存json\naccessToken | boolean | true | 设置是否需要验证token\ncacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式\ncacheTime | long | -2(使用全局设置） | 设置缓存有效时间\ncacheKeyIndex | int | -1(所有参数） | 作为缓存key的请求参数索引\n\n2.使用XHttpProxy进行请求。\n\n构建一个XHttpProxy，将定义的api接口传入后，直接调用接口进行请求。\n\n构造XHttpProxy可以传入`ThreadType`,默认是`ThreadType.TO_MAIN`。\n\n* TO_MAIN: executeToMain(main  -\u003e io -\u003e main)\n\n\u003e 【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程)，且响应的线程又切换至主线程】\n\n* TO_IO: executeToIO(main  -\u003e io -\u003e io)\n\n\u003e 【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程)，不过响应的线程不变，还是之前请求的那个io线程】\n\n* IN_THREAD: executeInThread(io  -\u003e io -\u003e io)\n\n\u003e 【注意】请确保网络请求在子线程中才可以使用该类型【实质是不做任何线程调度的同步请求】\n\n3.请求使用演示。\n\n```\n//使用XHttpProxy进行接口代理请求\nXHttpProxy.proxy(TestApi.IOrder.class)\n        .buyBook(mBookAdapter.getItem(position).getBookId(), UserManager.getInstance().getUser().getUserId(), 1)\n        .subscribeWith(new TipRequestSubscriber\u003cBoolean\u003e() {\n            @Override\n            public void onSuccess(Boolean aBoolean) {\n                ToastUtils.toast(\"图书购买\" + (aBoolean ? \"成功\" : \"失败\") + \"！\");\n                mRefreshLayout.autoRefresh();\n            }\n        });\n```\n\n---\n\n### 4、文件上传和下载\n\n1.文件上传【multipart/form-data】\n\n使用post的文件表单上传。使用`XHttp.post`,然后使用`params`传递附带的参数，使用`uploadFile`传递需要上传的文件，使用示例如下:\n\n```\nmIProgressLoader.updateMessage(\"上传中...\");\nXHttp.post(\"/book/uploadBookPicture\")\n        .params(\"bookId\", book.getBookId())\n        .uploadFile(\"file\", FileUtils.getFileByPath(mPicturePath), new IProgressResponseCallBack() {\n            @Override\n            public void onResponseProgress(long bytesWritten, long contentLength, boolean done) {\n\n            }\n        }).execute(Boolean.class)\n        .compose(RxLifecycle.with(this).\u003cBoolean\u003ebindToLifecycle())\n        .subscribeWith(new ProgressLoadingSubscriber\u003cBoolean\u003e(mIProgressLoader) {\n            @Override\n            public void onSuccess(Boolean aBoolean) {\n                mIsEditSuccess = true;\n                ToastUtils.toast(\"图片上传\" + (aBoolean ? \"成功\" : \"失败\") + \"！\");\n            }\n        });\n```\n\n2.文件下载\n\n使用`XHttp.downLoad`，传入下载的地址url、保存文件的路径以及文件名即可完成文件的下载，使用示例如下:\n\n```\nXHttp.downLoad(BookAdapter.getBookImgUrl(book))\n        .savePath(PathUtils.getExtPicturesPath())\n        .execute(new DownloadProgressCallBack\u003cString\u003e() {\n            @Override\n            public void onStart() {\n                HProgressDialogUtils.showHorizontalProgressDialog(getContext(), \"图片下载中...\", true);\n            }\n\n            @Override\n            public void onError(ApiException e) {\n                ToastUtils.toast(e.getMessage());\n                HProgressDialogUtils.cancel();\n            }\n\n            @Override\n            public void update(long bytesRead, long contentLength, boolean done) {\n                HProgressDialogUtils.onLoading(contentLength, bytesRead); //更新进度条\n            }\n\n            @Override\n            public void onComplete(String path) {\n                ToastUtils.toast(\"图片下载成功, 保存路径:\" + path);\n                HProgressDialogUtils.cancel();\n            }\n        });\n```\n\n---\n\n## 高阶网络请求操作\n\n### 请求生命周期绑定\n\n1.请求loading加载和请求生命周期绑定\n\n在请求时，订阅`ProgressLoadingSubscriber`或者`ProgressLoadingCallBack`，传入请求消息加载者`IProgressLoader`，即可完成生命周期的绑定。示例如下：\n\n```\nXHttpRequest req = ApiProvider.getAddUserReq(getRandomUser());\n    XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber\u003cBoolean\u003e(mIProgressLoader) {\n        @Override\n        public void onSuccess(Boolean aBoolean) {\n            ToastUtils.toast(\"用户添加成功！\");\n            mRefreshLayout.autoRefresh();\n        }\n    });\n```\n\n2.网络请求生命周期和Activity/Fragment生命周期绑定\n\n(1)这里需要依赖一下RxUtil2\n```\nimplementation 'com.github.xuexiangjys:rxutil2:1.1.2'\n```\n\n(2)在所在的Activity的onCreate()下锁定Activity.\n\n```\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    RxLifecycle.injectRxLifecycle(this);\n}\n```\n\n(3)然后在请求中使用RxJava的`compose`的操作符进行绑定。\n\n```\n.compose(RxLifecycle.with(this).\u003cBoolean\u003ebindToLifecycle())\n```\n\n### 拦截器\n\n#### 日志拦截器\n\n(1)框架默认提供一个实现好的日志拦截器`HttpLoggingInterceptor`,通过`XHttpSDK.debug(\"XHttp\");`就可以设置进去，它有5种打印模式\n\n* NONE: 不打印log\n\n* BASIC: 只打印\"请求首行\"和\"响应首行\"。\n\n* HEADERS: 打印请求和响应的所有 Header\n\n* PARAM: 只打印请求和响应参数\n\n* BODY: 打印所有数据(默认是这种)\n\n(2)如果需要对网络请求的相关参数进行自定义记录的话，可以继承`HttpLoggingInterceptor`实现一个自己的网络请求日志拦截器，重写`logForRequest`和`logForResponse`两个方法即可。\n\n(3)设置自定义的日志拦截器.\n```\nXHttpSDK.debug(new CustomLoggingInterceptor());\n```\n\n#### 动态参数添加拦截器\n\n\u003e 有时候，我们需要对所有请求添加一些固定的请求参数，但是这些参数的值又是变化的，这个时候我们就需要动态添加请求参数【例如，请求的token、时间戳以及签名等】\n\n(1)继承`BaseDynamicInterceptor`，实现`updateDynamicParams`方法，如下：\n\n```\n@Override\nprotected TreeMap\u003cString, Object\u003e updateDynamicParams(TreeMap\u003cString, Object\u003e dynamicMap) {\n    if (isAccessToken()) {//是否添加token\n        dynamicMap.put(\"token\", TokenManager.getInstance().getToken());\n    }\n    if (isSign()) {//是否添加签名\n        dynamicMap.put(\"sign\", TokenManager.getInstance().getSign());\n    }\n    if (isTimeStamp()) {//是否添加请求时间戳\n        dynamicMap.put(\"timeStamp\", DateUtils.getNowMills());\n    }\n    return dynamicMap;//dynamicMap:是原有的全局参数+局部参数+新增的动态参数\n}\n```\n\n(2)设置动态参数添加拦截器。\n\n```\nXHttpSDK.addInterceptor(new CustomDynamicInterceptor()); //设置动态参数添加拦截器\n```\n\n#### 失效请求校验拦截器\n\n\u003e 当服务端返回一些独特的错误码（一般是token校验错误、失效，请求过于频繁等），需要我们进行全局性的拦截捕获，并作出相应的响应时，我们就需要定义一个特殊的拦截器求处理这些请求。\n\n(1)继承`BaseExpiredInterceptor`，实现`isResponseExpired`和`responseExpired`方法，如下：\n\n```\n/**\n * 判断是否是失效的响应\n *\n * @param oldResponse\n * @param bodyString\n * @return {@code true} : 失效 \u003cbr\u003e  {@code false} : 有效\n */\n@Override\nprotected ExpiredInfo isResponseExpired(Response oldResponse, String bodyString) {\n    int code = JSONUtils.getInt(bodyString, ApiResult.CODE, 0);\n    ExpiredInfo expiredInfo = new ExpiredInfo(code);\n    switch (code) {\n        case TOKEN_INVALID:\n        case TOKEN_MISSING:\n            expiredInfo.setExpiredType(KEY_TOKEN_EXPIRED)\n                    .setBodyString(bodyString);\n            break;\n        case AUTH_ERROR:\n            expiredInfo.setExpiredType(KEY_UNREGISTERED_USER)\n                    .setBodyString(bodyString);\n            break;\n        default:\n            break;\n    }\n    return expiredInfo;\n}\n\n/**\n * 失效响应的处理\n *\n * @return 获取新的有效请求响应\n */\n@Override\nprotected Response responseExpired(Response oldResponse, Chain chain, ExpiredInfo expiredInfo) {\n    switch(expiredInfo.getExpiredType()) {\n        case KEY_TOKEN_EXPIRED:\n            User user = TokenManager.getInstance().getLoginUser();\n            if (user != null) {\n                final boolean[] isGetNewToken = {false};\n                HttpLog.e(\"正在重新获取token...\");\n                XHttpProxy.proxy(TestApi.IAuthorization.class, ThreadType.IN_THREAD)\n                        .login(user.getLoginName(), user.getPassword())\n                        .subscribeWith(new NoTipRequestSubscriber\u003cLoginInfo\u003e() {\n                            @Override\n                            protected void onSuccess(LoginInfo loginInfo) {\n                                TokenManager.getInstance()\n                                        .setToken(loginInfo.getToken())\n                                        .setLoginUser(loginInfo.getUser());\n                                isGetNewToken[0] = true;\n                                HttpLog.e(\"重新获取token成功：\" + loginInfo.getToken());\n                            }\n                        });\n                if (isGetNewToken[0]) {\n                    try {\n                        HttpLog.e(\"使用新的token重新进行请求...\");\n                        return chain.proceed(HttpUtils.updateUrlParams(chain.request(), \"token\", TokenManager.getInstance().getToken()));\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n            } else {\n                XRouter.getInstance().build(\"/xhttp/login\").navigation();\n                return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), \"请先进行登录！\");\n            }\n            break;\n        case KEY_UNREGISTERED_USER:\n            return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), \"非法用户登录！\");\n        default:\n            break;\n    }\n    return null;\n}\n\n```\n\n(2)设置失效请求校验拦截器。\n\n```\nXHttpSDK.addInterceptor(new CustomExpiredInterceptor()); //请求失效校验拦截器\n```\n\n### 自定义API请求\n\n#### 自定义请求响应的API结构\n\n如果你不想使用默认的ApiResult实体作为统一的服务端响应实体，比如说你想要下面的响应实体:\n\n```\nprivate int errorCode; //请求的错误码\nprivate String errorInfo; //请求错误的原因描述\nprivate T result; //请求的结果\nprivate long timeStamp; //服务端返回的时间戳\n```\n\n(1)首先，继承`ApiResult`实体，重写其`getCode`、`getMsg`、`isSuccess`、`getData`和`setData`方法。\n\n```\npublic class CustomApiResult\u003cT\u003e extends ApiResult\u003cT\u003e {\n\n    private int errorCode;\n    private String errorInfo;\n    private T result;\n    private long timeStamp;\n\n    public int getErrorCode() {\n        return errorCode;\n    }\n\n    public CustomApiResult\u003cT\u003e setErrorCode(int errorCode) {\n        this.errorCode = errorCode;\n        return this;\n    }\n\n    public String getErrorInfo() {\n        return errorInfo;\n    }\n\n    public CustomApiResult\u003cT\u003e setErrorInfo(String errorInfo) {\n        this.errorInfo = errorInfo;\n        return this;\n    }\n\n    public T getResult() {\n        return result;\n    }\n\n    public CustomApiResult\u003cT\u003e setResult(T result) {\n        this.result = result;\n        return this;\n    }\n\n    public long getTimeStamp() {\n        return timeStamp;\n    }\n\n    public CustomApiResult\u003cT\u003e setTimeStamp(long timeStamp) {\n        this.timeStamp = timeStamp;\n        return this;\n    }\n\n    @Override\n    public int getCode() {\n        return errorCode;\n    }\n\n    @Override\n    public String getMsg() {\n        return errorInfo;\n    }\n\n    @Override\n    public boolean isSuccess() {\n        return errorCode == 0;\n    }\n\n    @Override\n    public void setData(T data) {\n        result = data;\n    }\n\n    @Override\n    public T getData() {\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"ApiResult{\" +\n                \"errorCode='\" + errorCode + '\\'' +\n                \", errorInfo='\" + errorInfo + '\\'' +\n                \", timeStamp='\" + timeStamp + '\\'' +\n                \", result=\" + result +\n                '}';\n    }\n}\n```\n\n(2)进行请求的时候使用`execute(CallBackProxy)`或者`execute(CallClazzProxy`方法进行请求\n\n```\nXHttp.get(\"/test/testCustomResult\")\n            .execute(new CallBackProxy\u003cCustomApiResult\u003cBoolean\u003e, Boolean\u003e(new TipRequestCallBack\u003cBoolean\u003e() {\n                @Override\n                public void onSuccess(Boolean response) throws Throwable {\n                    ToastUtils.toast(\"请求成功：\" + response);\n                }\n            }){});  //千万注意，这里的{}一定不能去掉，否则解析错误\n```\n\n【注意】上面提示的{}一定不能去掉，否则解析错误, 会报\"ApiResult.class.isAssignableFrom(cls) err!!\"的错误。\n\n如果你觉得写一长串比较麻烦，你可以自定义请求继承你需要的请求方式，例如这里是get请求，我们可以这样写:\n\n```\npublic class CustomGetRequest extends GetRequest {\n\n    public CustomGetRequest(String url) {\n        super(url);\n    }\n\n    @Override\n    public \u003cT\u003e Observable\u003cT\u003e execute(Type type) {\n        return execute(new CallClazzProxy\u003cCustomApiResult\u003cT\u003e, T\u003e(type) {\n        });\n    }\n\n    @Override\n    public \u003cT\u003e Disposable execute(CallBack\u003cT\u003e callBack) {\n        return execute(new CallBackProxy\u003cCustomApiResult\u003cT\u003e, T\u003e(callBack) {\n        });\n    }\n}\n```\n\n然后我们就可以用自定义的`CustomGetRequest`进行请求了,是不是简化了很多呢。\n\n```\nnew CustomGetRequest(\"/test/testCustomResult\")\n        .execute(new TipRequestCallBack\u003cBoolean\u003e() {\n            @Override\n            public void onSuccess(Boolean response) throws Throwable {\n                ToastUtils.toast(\"请求成功：\" + response);\n            }\n        });\n```\n\n\n#### 使用自定义的retrofit接口\n\n如果你对retrofit接口情有独钟，我也提供了相应的api方便调用.\n\n1.定义retrofit接口。例如我定义一个用户添加的接口:\n\n```\n/**\n * 使用的是retrofit的接口定义\n */\npublic interface UserService {\n    @POST(\"/user/registerUser/\")\n    @Headers({\"Content-Type: application/json\", \"Accept: application/json\"})\n    Observable\u003cApiResult\u003cBoolean\u003e\u003e registerUser(@Body RequestBody jsonBody);\n\n\n    @POST(\"/user/registerUser/\")\n    @Headers({\"Content-Type: application/json\", \"Accept: application/json\"})\n    Observable\u003cApiResult\u003e register(@Body User user);\n}\n```\n\n2.使用`XHttp.custom()`构建的`CustomRequest`进行请求，你可以使用`apiCall`和`call`进行请求。\n\n* apiCall: 针对的是retrofit定义的接口，返回的是Observable\u003cApiResult\u003cT\u003e\u003e的情况，对ApiResult进行拆包，直接获取数据。对于上面定义的第一个接口`registerUser`。\n\n* call: 针对的是retrofit定义的接口，返回的是Observable\u003cT\u003e的情况，不对ApiResult进行拆包。对于上面定义的第二个接口`register`。\n\n使用示例如下:\n\n```\nCustomRequest request = XHttp.custom();\nrequest.apiCall(request.create(TestApi.UserService.class)\n        .registerUser(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser())))\n        .subscribeWith(new TipRequestSubscriber\u003cBoolean\u003e() {\n            @Override\n            protected void onSuccess(Boolean aBoolean) {\n                ToastUtils.toast(\"添加用户成功!\");\n            }\n        });\n```\n\n```\nCustomRequest request = XHttp.custom();\nrequest.call(request.create(TestApi.UserService.class)\n        .register(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser())))\n        .subscribeWith(new TipRequestSubscriber\u003cApiResult\u003e() {\n            @Override\n            protected void onSuccess(ApiResult apiResult) {\n                ToastUtils.toast(\"添加用户成功!\");\n                showResult(JsonUtil.toJson(apiResult));\n            }\n        });\n```\n\n### 缓存策略\n\n目前框架提供了如下8种缓存策略:\n\n* NO_CACHE: 不使用缓存(默认方式)\n\n* DEFAULT: 完全按照HTTP协议的默认缓存规则，走OKhttp的Cache缓存\n\n* FIRST_REMOTE: 先请求网络，请求网络失败后再加载缓存\n\n* FIRST_CACHE: 先加载缓存，缓存没有再去请求网络\n\n* ONLY_REMOTE: 仅加载网络，但数据依然会被缓存\n\n* ONLY_CACHE: 只读取缓存\n\n* CACHE_REMOTE: 先使用缓存，不管是否存在，仍然请求网络，会回调两次\n\n* CACHE_REMOTE_DISTINCT: 先使用缓存，不管是否存在，仍然请求网络，会先把缓存回调给你，等网络请求回来发现数据是一样的就不会再返回，否则再返回（这样做的目的是防止数据是一样的你也需要刷新界面）\n\n对于缓存的实现，提供了磁盘缓存`LruDiskCache`和内存缓存`LruMemoryCache`两种实现，默认使用的是磁盘缓存。\n\n(1)可以先进行缓存的全局性配置，配置缓存的有效期、缓存大小，缓存路径、序列化器等。\n\n```\nXHttp.getInstance()\n        .setIsDiskCache(true) //设置使用磁盘缓存\n        .setCacheTime(60 * 1000) //设置全局缓存有效期为一分钟\n        .setCacheVersion(1) //设置全局缓存的版本\n        .setCacheDirectory(Utils.getDiskCacheDir(this, \"XHttp\")) //设置全局缓存保存的目录路径\n        .setCacheMode(CacheMode.NO_CACHE) //设置全局的缓存策略\n        .setCacheDiskConverter(new GsonDiskConverter())//默认缓存使用序列化转化\n        .setCacheMaxSize(50 * 1024 * 1024);//设置缓存大小为50M\n```\n\n(2)在进行请求的时候，设置缓存模式和缓存的key即可。如下:\n\n```\nXHttp.get(\"/book/getAllBook\")\n        .timeOut(10 * 1000)//测试局部超时10s\n        .cacheMode(mCacheMode)\n        .cacheKey(CACHE_KEY)//缓存key\n        .retryCount(5)//重试次数\n        .cacheTime(5 * 60)//缓存时间300s，默认-1永久缓存  okhttp和自定义缓存都起作用\n        .cacheDiskConverter(new GsonDiskConverter())//默认使用的是 new SerializableDiskConverter();\n        .timeStamp(true)\n        .execute(new ProgressLoadingCallBack\u003cCacheResult\u003cList\u003cBook\u003e\u003e\u003e(mIProgressLoader) {\n            @Override\n            public void onSuccess(CacheResult\u003cList\u003cBook\u003e\u003e cacheResult) {\n                ToastUtils.toast(\"请求成功!\");\n                String from;\n                if (cacheResult.isFromCache) {\n                    from = \"我来自缓存\";\n                } else {\n                    from = \"我来自远程网络\";\n                }\n                showResult(from + \"\\n\" + JsonUtil.toJson(cacheResult.data));\n            }\n\n            @Override\n            public void onError(ApiException e) {\n                super.onError(e);\n                ToastUtils.toast(e.getDisplayMessage());\n            }\n        });\n```\n\n\n--------------\n\n## 混淆配置\n\n```\n#XHttp2\n-keep class com.xuexiang.xhttp2.model.** { *; }\n-keep class com.xuexiang.xhttp2.cache.model.** { *; }\n-keep class com.xuexiang.xhttp2.cache.stategy.**{*;}\n-keep class com.xuexiang.xhttp2.annotation.** { *; }\n\n#okhttp\n-dontwarn com.squareup.okhttp3.**\n-keep class com.squareup.okhttp3.** { *;}\n-dontwarn okio.**\n-dontwarn javax.annotation.Nullable\n-dontwarn javax.annotation.ParametersAreNonnullByDefault\n-dontwarn javax.annotation.**\n\n# Retrofit\n-dontwarn retrofit2.**\n-keep class retrofit2.** { *; }\n-keepattributes Exceptions\n\n# RxJava RxAndroid\n-dontwarn sun.misc.**\n-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {\n    long producerIndex;\n    long consumerIndex;\n}\n-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {\n    rx.internal.util.atomic.LinkedQueueNode producerNode;\n}\n-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {\n    rx.internal.util.atomic.LinkedQueueNode consumerNode;\n}\n\n#如果用到Gson解析包的，直接添加下面这几行就能成功混淆，不然会报错\n-keepattributes Signature\n-keep class com.google.gson.stream.** { *; }\n-keepattributes EnclosingMethod\n-keep class org.xz_sale.entity.**{*;}\n-keep class com.google.gson.** {*;}\n-keep class com.google.**{*;}\n-keep class sun.misc.Unsafe { *; }\n-keep class com.google.gson.stream.** { *; }\n-keep class com.google.gson.examples.android.model.** { *; }\n```\n\n---\n\n## 特别感谢\n\nhttps://github.com/zhou-you/RxEasyHttp\n\n## 如果觉得项目还不错，可以考虑打赏一波\n\n\u003e 你的打赏是我维护的动力，我将会列出所有打赏人员的清单在下方作为凭证，打赏前请留下打赏项目的备注！\n\n![pay.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/pay/pay.png)\n\n感谢下面小伙伴的打赏：\n\n姓名 | 金额 | 方式\n:-|:-|:-\n*声 | 50￥ | 微信\n**东 | 5￥ | 支付宝\n\n## 联系方式\n\n[![](https://img.shields.io/badge/点击一键加入QQ交流群-602082750-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=9922861ef85c19f1575aecea0e8680f60d9386080a97ed310c971ae074998887)\n\n\u003e 更多资讯内容，欢迎扫描关注我的个人微信公众号:【我的Android开源之旅】\n\n![](https://s1.ax1x.com/2022/04/27/LbGMJH.jpg)\n\n[demo-gif]: ./img/demo.gif\n[download-svg]: https://img.shields.io/badge/downloads-2.61M-blue.svg\n[download-url]: https://github.com/xuexiangjys/XHttp2/blob/master/apk/xhttp2_demo_1.0.apk?raw=true\n[download-img]: ./img/download.png\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxuexiangjys%2Fxhttp2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxuexiangjys%2Fxhttp2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxuexiangjys%2Fxhttp2/lists"}