{"id":15028310,"url":"https://github.com/zhou-you/rxeasyhttp","last_synced_at":"2025-05-15T05:07:28.517Z","repository":{"id":39459705,"uuid":"93617825","full_name":"zhou-you/RxEasyHttp","owner":"zhou-you","description":"本库是一款基于RxJava2+Retrofit2实现简单易用的网络请求框架，结合android平台特性的网络封装库,采用api链式调用一点到底,集成cookie管理,多种缓存模式,极简https配置,上传下载进度显示,请求错误自动重试,请求携带token、时间戳、签名sign动态配置,自动登录成功后请求重发功能,3种层次的参数设置默认全局局部,默认标准ApiResult同时可以支持自定义的数据结构，已经能满足现在的大部分网络请求。","archived":false,"fork":false,"pushed_at":"2023-09-12T07:14:40.000Z","size":52857,"stargazers_count":3131,"open_issues_count":32,"forks_count":613,"subscribers_count":73,"default_branch":"master","last_synced_at":"2025-05-15T05:07:20.887Z","etag":null,"topics":["okhttp3","retrofit2","rxjava"],"latest_commit_sha":null,"homepage":"","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/zhou-you.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-06-07T09:33:47.000Z","updated_at":"2025-05-12T06:22:16.000Z","dependencies_parsed_at":"2022-07-20T02:49:06.345Z","dependency_job_id":"ab9a17c3-a953-4a3f-82e4-7e7e86f1c97d","html_url":"https://github.com/zhou-you/RxEasyHttp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhou-you%2FRxEasyHttp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhou-you%2FRxEasyHttp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhou-you%2FRxEasyHttp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhou-you%2FRxEasyHttp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhou-you","download_url":"https://codeload.github.com/zhou-you/RxEasyHttp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254276447,"owners_count":22043867,"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":["okhttp3","retrofit2","rxjava"],"created_at":"2024-09-24T20:08:00.114Z","updated_at":"2025-05-15T05:07:23.506Z","avatar_url":"https://github.com/zhou-you.png","language":"Java","readme":"## RxEasyHttp\n\n本库是一款基于RxJava2+Retrofit2实现简单易用的网络请求框架，结合android平台特性的网络封装库,采用api链式调用一点到底,集成cookie管理,多种缓存模式,极简https配置,上传下载进度显示,请求错误自动重试,请求携带token、时间戳、签名sign动态配置,自动登录成功后请求重发功能,3种层次的参数设置默认全局局部,默认标准ApiResult同时可以支持自定义的数据结构，已经能满足现在的大部分网络请求。\n*注：Retrofit和Rxjava是当下非常火爆的开源框架，均来自神一般的公司。本库就不介绍Retrofit和Rxjava2的用法。*\n\n## 为什么会封装此库？\n网上好的开源网络库像Volley、async-http、okhttp、retrofit等都非常强大，但是实际应用中我们不会直接去使用，一般都会根据自己的业务再封装一层，这样更方便快捷，又能统一处理业务共性的东西例如：统一的数据结构（code、msg、data）、token处理、网络异常等情况。在使用retrofit来请求网络的时候，项目的需求越来越多，api也随之越来越多，一个普通的应用api一般也在100+左右。如果把这些api放在一个ApiService内会很臃肿，不利于查看api.如果采用模块的方式对api进行分类，每个模块对应若干个api.以retrofit的使用方式又需要创建若干个ApiService，这种方式维护方便，但是模块增多了，类也增多了很多。对于懒人来说就想通过一个URL就能回调你所需要的数据，什么ApiService都不想理会，同时又可以很快的与自己的业务相关联，就类似于代替你在开源网络库基础上再封装一层的作用，于是本库就应运而生。\n\n## 特点\n- 比Retrofit使用更简单、更易用。\n- 采用链式调用一点到底\n- 加入基础ApiService，减少Api冗余\n- 支持动态配置和自定义底层框架Okhttpclient、Retrofit.\n- 支持多种方式访问网络GET、POST、PUT、DELETE等请求协议\n- 支持网络缓存,八种缓存策略可选,涵盖大多数业务场景\n- 支持固定添加header和动态添加header\n- 支持添加全局参数和动态添加局部参数\n- 支持文件下载、多文件上传和表单提交数据\n- 支持文件请求、上传、下载的进度回调、错误回调，也可以自定义回调\n- 支持默认、全局、局部三个层次的配置功能\n- 支持任意数据结构的自动解析\n- 支持添加动态参数例如timeStamp时间戳、token、签名sign\n- 支持自定义的扩展API\n- 支持多个请求合并\n- 支持Cookie管理\n- 支持异步、同步请求\n- 支持Https、自签名网站Https的访问、双向验证\n- 支持失败重试机制，可以指定重试次数、重试间隔时间\n- 支持根据ky删除网络缓存和清空网络缓存\n- 提供默认的标准ApiResult解析和回调，并且可自定义ApiResult\n- 支持取消数据请求，取消订阅，带有对话框的请求不需要手动取消请求，对话框消失会自动取消请求\n- 支持请求数据结果采用回调和订阅两种方式\n- api设计上结合http协议和android平台特点来实现,loading对话框,实时进度条显示\n- 返回结果和异常统一处理\n- 结合RxJava2，线程智能控制\n\n## 关于我\n[![github](https://img.shields.io/badge/GitHub-zhou--you-green.svg)](https://github.com/zhou-you)   [![csdn](https://img.shields.io/badge/CSDN-zhouy478319399-green.svg)](http://blog.csdn.net/zhouy478319399)\n## 联系方式\n本群旨在为使用我github项目的人提供方便，如果遇到问题欢迎在群里提问。\n\n#### 欢迎加入QQ交流群（Q1群已满，请加入Q2群）\n\n[![](https://img.shields.io/badge/%E7%82%B9%E6%88%91%E4%B8%80%E9%94%AE%E5%8A%A0%E5%85%A5Q1%E7%BE%A4-581235049%28%E5%B7%B2%E6%BB%A1%29-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=1e1f4bcfd8775a55e6cf6411f6ff0e7058ff469ef87c4d1e67890c27f0c5a390)\n\n[![](https://img.shields.io/badge/%E7%82%B9%E6%88%91%E4%B8%80%E9%94%AE%E5%8A%A0%E5%85%A5Q2%E7%BE%A4-832887601-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=f3c997d1c3cc6a8c9fa46d3fde0d663f50e4e6d0e6441b8cc276bef39befd24c)\n\n![](http://img.blog.csdn.net/20170601165330238)![](https://img-blog.csdnimg.cn/20190627164802234.jpg)\n## 演示（请star支持）\n![](https://github.com/zhou-you/RxEasyHttp/raw/master/screenshot/1.gif) ![](https://github.com/zhou-you/RxEasyHttp/raw/master/screenshot/2.gif)\n![](https://github.com/zhou-you/RxEasyHttp/raw/master/screenshot/3.gif) ![](https://github.com/zhou-you/RxEasyHttp/raw/master/screenshot/4.gif)\n\n### RxEasyHttp与Rxjava结合使用场景演示\n![](https://github.com/zhou-you/RxEasyHttp/raw/master/screenshot/5.gif)\n\n**[RxEasyHttp网络库与Rxjava2结合常见使用场景介绍    点我！！！\u003e\u003e](http://blog.csdn.net/zhouy478319399/article/details/78550248)**\n\n[![](https://badge.juejin.im/entry/5a0d4d0d6fb9a045080934f1/likes.svg?style=plastic)](https://juejin.im/post/5a0d4cd851882531ba108090)\n\n## 版本说明\n\n### 当前版本\n[![release](https://img.shields.io/badge/release-V2.1.2-orange.svg)](https://github.com/zhou-you/RxEasyHttp/blob/master/update.md)\n\n**[历史版本，点我、点我、点我\u003e\u003e](https://github.com/zhou-you/RxEasyHttp/blob/master/update.md)**\n\n## 用法介绍\n目前只支持主流开发工具AndtoidStudio的使用，没有提供Eclipse使用方式.\n本项目Demo的网络请求的服务器地址为了安全，把url去掉了，但是Demo程序中的示例都是ok的\n### 点击按钮下载Demo\n[![downloads](https://img.shields.io/badge/downloads-2.2M-blue.svg)](https://github.com/zhou-you/RxEasyHttp/blob/master/apk/rxeasyhttp-demo.apk?raw=true) \n#### 扫码下载Demo\n![](https://github.com/zhou-you/RxEasyHttp/blob/master/screenshot/down.png?raw=true)\n\n### build.gradle设置\n```\ndependencies {\n compile 'com.zhouyou:rxeasyhttp:2.1.5'\n}\n```\n想查看所有版本，请点击下面地址。\n\n[![jcenter](https://img.shields.io/badge/Jcenter-Latest%20Release-orange.svg)](https://jcenter.bintray.com/com/zhouyou/rxeasyhttp/)\n## 权限说明\n如果使用本库实现文件下载到SD卡、或者配置了缓存数据到SD卡，你必须要考虑到Android6.0及以上系统的运行时权限，给大家推荐两个权限库：\n\n[AndPermission](https://github.com/yanzhenjie/AndPermission)  \n[RxPermissions](https://github.com/tbruyelle/RxPermissions) \n\n因为要请求网络、从SD卡读写缓存、下载文件到SD卡等等，所以需要在manifest.xml中配置以下几个权限，如果你已经配置过了这些权限，请不要重复配置：\n```\n\u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/\u003e\n\u003cuses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/\u003e\n\u003cuses-permission android:name=\"android.permission.INTERNET\"/\u003e\n```\n## 全局配置\n一般在 Aplication，或者基类中，只需要调用一次即可，可以配置调试开关，全局的超时时间，公共的请求头和请求参数等信息\n初始化需要一个Context，最好在Application#onCreate()中初始化，记得在manifest.xml中注册Application。\n#### Application:\n\n```\npublic class MyApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n    }\n}\n```\n#### manifest.xml：\n\n```\n...\n\n\u003capplication\n    android:name=\".MyApplication\"\n    ...\n /\u003e\n```\n\n## 默认初始化\n如果使用默认始化后，一切采用默认设置。如果你需要配置全局超时时间、缓存、Cookie、底层为OkHttp的话，请看[高级初始化](https://github.com/zhou-you/RxEasyHttp#高级初始化)。\n```\npublic class MyApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        EasyHttp.init(this);//默认初始化\n    }\n}\n```\n## 高级初始化\n可以进行超时配置、网络缓存配置、okhttp相关参数配置、retrofit相关参数配置、cookie配置等，这些参数可以选择性的根据业务需要配置。\n```\npublic class MyApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        EasyHttp.init(this);//默认初始化,必须调用\n\n        //全局设置请求头\n        HttpHeaders headers = new HttpHeaders();\n        headers.put(\"User-Agent\", SystemInfoUtils.getUserAgent(this, AppConstant.APPID));\n        //全局设置请求参数\n        HttpParams params = new HttpParams();\n        params.put(\"appId\", AppConstant.APPID);\n\n        //以下设置的所有参数是全局参数,同样的参数可以在请求的时候再设置一遍,那么对于该请求来讲,请求中的参数会覆盖全局参数\n        EasyHttp.getInstance()\n        \n                //可以全局统一设置全局URL\n                .setBaseUrl(Url)//设置全局URL  url只能是域名 或者域名+端口号 \n\n                // 打开该调试开关并设置TAG,不需要就不要加入该行\n                // 最后的true表示是否打印内部异常，一般打开方便调试错误\n                .debug(\"EasyHttp\", true)\n                \n                //如果使用默认的60秒,以下三行也不需要设置\n                .setReadTimeOut(60 * 1000)\n                .setWriteTimeOut(60 * 100)\n                .setConnectTimeout(60 * 100)\n                \n                //可以全局统一设置超时重连次数,默认为3次,那么最差的情况会请求4次(一次原始请求,三次重连请求),\n                //不需要可以设置为0\n                .setRetryCount(3)//网络不好自动重试3次\n                //可以全局统一设置超时重试间隔时间,默认为500ms,不需要可以设置为0\n                .setRetryDelay(500)//每次延时500ms重试\n                //可以全局统一设置超时重试间隔叠加时间,默认为0ms不叠加\n                .setRetryIncreaseDelay(500)//每次延时叠加500ms\n                \n                //可以全局统一设置缓存模式,默认是不使用缓存,可以不传,具体请看CacheMode\n                .setCacheMode(CacheMode.NO_CACHE)\n                //可以全局统一设置缓存时间,默认永不过期\n                .setCacheTime(-1)//-1表示永久缓存,单位:秒 ，Okhttp和自定义RxCache缓存都起作用\n                //全局设置自定义缓存保存转换器，主要针对自定义RxCache缓存\n                .setCacheDiskConverter(new SerializableDiskConverter())//默认缓存使用序列化转化\n                //全局设置自定义缓存大小，默认50M\n                .setCacheMaxSize(100 * 1024 * 1024)//设置缓存大小为100M\n                //设置缓存版本，如果缓存有变化，修改版本后，缓存就不会被加载。特别是用于版本重大升级时缓存不能使用的情况\n                .setCacheVersion(1)//缓存版本为1\n                //.setHttpCache(new Cache())//设置Okhttp缓存，在缓存模式为DEFAULT才起作用\n                \n                //可以设置https的证书,以下几种方案根据需要自己设置\n                .setCertificates()                                  //方法一：信任所有证书,不安全有风险\n                //.setCertificates(new SafeTrustManager())            //方法二：自定义信任规则，校验服务端证书\n                //配置https的域名匹配规则，不需要就不要加入，使用不当会导致https握手失败\n                //.setHostnameVerifier(new SafeHostnameVerifier())\n                //.addConverterFactory(GsonConverterFactory.create(gson))//本框架没有采用Retrofit的Gson转化，所以不用配置\n                .addCommonHeaders(headers)//设置全局公共头\n                .addCommonParams(params)//设置全局公共参数\n                //.addNetworkInterceptor(new NoCacheInterceptor())//设置网络拦截器\n                //.setCallFactory()//局设置Retrofit对象Factory\n                //.setCookieStore()//设置cookie\n                //.setOkproxy()//设置全局代理\n                //.setOkconnectionPool()//设置请求连接池\n                //.setCallbackExecutor()//全局设置Retrofit callbackExecutor\n                //可以添加全局拦截器，不需要就不要加入，错误写法直接导致任何回调不执行\n                //.addInterceptor(new GzipRequestInterceptor())//开启post数据进行gzip后发送给服务器\n                .addInterceptor(new CustomSignInterceptor());//添加参数签名拦截器\n    }\n}\n```\n## 请求数据\n网络请求，采用链式调用，支持一点到底。\n\n### 入口方法\n```\n  /**\n     * get请求\n     */\n    public static GetRequest get(String url);\n\n    /**\n     * post请求和文件上传\n     */\n    public static PostRequest post(String url);\n\n    /**\n     * delete请求\n     */\n    public static DeleteRequest delete(String url) ;\n\n    /**\n     * 自定义请求\n     */\n    public static CustomRequest custom();\n\n    /**\n     * 文件下载\n     */\n    public static DownloadRequest downLoad(String url) ;\n\n    /**\n     * put请求\n     */\n    public static PutRequest put(String url);\n```\n\n\n### 通用功能配置\n1.包含一次普通请求所有能配置的参数，真实使用时不需要配置这么多，按自己的需要选择性的使用即可\u003cbr/\u003e\n2.以下配置全部是单次请求配置，不会影响全局配置，没有配置的仍然是使用全局参数。\u003cbr/\u003e\n3.为单个请求设置超时，比如涉及到文件的需要设置读写等待时间多一点。\u003cbr/\u003e\n完整参数GET示例：\n```\nEasyHttp.get(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")\n                .baseUrl(\"http://www.xxxx.com\")//设置url\n                .writeTimeOut(30*1000)//局部写超时30s,单位毫秒\n                .readTimeOut(30*1000)//局部读超时30s,单位毫秒\n                .connectTimeout(30*1000)//局部连接超时30s,单位毫秒\n                .headers(new HttpHeaders(\"header1\",\"header1Value\"))//添加请求头参数\n                .headers(\"header2\",\"header2Value\")//支持添加多个请求头同时添加\n                .headers(\"header3\",\"header3Value\")//支持添加多个请求头同时添加\n                .params(\"param1\",\"param1Value\")//支持添加多个参数同时添加\n                .params(\"param2\",\"param2Value\")//支持添加多个参数同时添加\n                //.addCookie(new CookieManger(this).addCookies())//支持添加Cookie\n                .cacheTime(300)//缓存300s 单位s\n                .cacheKey(\"cachekey\")//缓存key\n                .cacheMode(CacheMode.CACHEANDREMOTE)//设置请求缓存模式\n                //.okCache()//使用模式缓存模式时，走Okhttp缓存\n                .cacheDiskConverter(new GsonDiskConverter())//GSON-数据转换器\n                //.certificates()添加证书\n                .retryCount(5)//本次请求重试次数\n                .retryDelay(500)//本次请求重试延迟时间500ms\n                .addInterceptor(Interceptor)//添加拦截器\n                .okproxy()//设置代理\n                .removeHeader(\"header2\")//移除头部header2\n                .removeAllHeaders()//移除全部请求头\n                .removeParam(\"param1\")\n                .accessToken(true)//本次请求是否追加token\n                .timeStamp(false)//本次请求是否携带时间戳\n                .sign(false)//本次请求是否需要签名\n                .syncRequest(true)//是否是同步请求，默认异步请求。true:同步请求\n                .execute(new CallBack\u003cSkinTestResult\u003e() {\n                    @Override\n                    public void onStart() {\n                        //开始请求\n                    }\n\n                    @Override\n                    public void onCompleted() {\n                       //请求完成\n                    }\n\n                    @Override\n                    public void onError(ApiException e) {\n                      //请求错误\n                    }\n\n                    @Override\n                    public void onSuccess(SkinTestResult response) {\n                      //请求成功\n                    }\n                });\n```\n#### url\nUrl可以通过初始化配置的时候传入`EasyHttp.getInstance().setBaseUrl(\"http://www.xxx.com\");`  \n入口方法传入： `EasyHttp.get(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\").baseUrl(\"http://www.xxxx.com\")`\n如果入口方法中传入的url含有http或者https,则不会拼接初始化设置的baseUrl.\n例如：`EasyHttp.get(\"http://www.xxx.com/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")`则setBaseUrl()和baseUrl()传入的baseurl都不会被拼接。\n*注:EasyHttp.get/post/put/等采用拼接的用法时请注意，url要用/斜杠开头，例如：`EasyHttp.get(\"/v1/login\")` 正确  ` EasyHttp.get(\"v1/login\")` 错误*\n#### http请求参数\n两种设置方式\n.params(HttpParams params)\n.params(\"param1\",\"param1Value\")//添加参数键值对\n\n HttpParams params = new HttpParams();\n params.put(\"appId\", AppConstant.APPID);\n .addCommonParams(params)//设置全局公共参数\n#### http请求头\n.headers(HttpHeaders headers) \n.headers(\"header2\",\"header2Value\")//添加参数键值对\n\n.addCommonHeaders(headers)//设置全局公共头\n\n### 普通网络请求\n**支持get/post/delete/put等**\n链式调用的终点请求的执行方式有：execute(Class\u003cT\u003e clazz) 、execute(Type type)、execute(CallBack\u003cT\u003e callBack)三种方式，都是针对标准的ApiResult\n#### execute(CallBack\u003cT\u003e callBack)\n1.EasyHttp（**推荐**）\n示例：\n```\n方式一：\n //EasyHttp.post(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")\n EasyHttp.get(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")\n                .readTimeOut(30 * 1000)//局部定义读超时\n                .writeTimeOut(30 * 1000)\n                .connectTimeout(30 * 1000)\n                .params(\"name\",\"张三\")\n                .timeStamp(true)\n                .execute(new SimpleCallBack\u003cSkinTestResult\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onSuccess(SkinTestResult response) {\n                        if (response != null) showToast(response.toString());\n                    }\n                });\n```\n2.手动创建请求对象\n```\n //GetRequest 、PostRequest、DeleteRequest、PutRequest\n GetRequest request = new GetRequest(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\");\n        request.readTimeOut(30 * 1000)//局部定义读超时\n                .params(\"param1\", \"param1Value1\")\n                .execute(new SimpleCallBack\u003cSkinTestResult\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n\n                    }\n\n                    @Override\n                    public void onSuccess(SkinTestResult response) {\n\n                    }\n                });\n```\n#### execute(Class\u003cT\u003e clazz)和execute(Type type)\nexecute(Class\u003cT\u003e clazz)和execute(Type type)功能基本一样，execute(Type type)主要是针对集合不能直接传递Class\n```\nEasyHttp.get(url)\n                .params(\"param1\", \"paramValue1\")\n                .execute(SkinTestResult.class)//非常简单直接传目标class\n                //.execute(new TypeToken\u003cList\u003cSectionItem\u003e\u003e() {}.getType())//Type类型\n                .subscribe(new BaseSubscriber\u003cSkinTestResult\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onNext(SkinTestResult skinTestResult) {\n                        showToast(skinTestResult.toString());\n                    }\n                });\n```\n### 请求返回Disposable\n网络请求会返回Disposable对象，方便取消网络请求\n```\nDisposable disposable = EasyHttp.get(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")\n                .params(\"param1\", \"paramValue1\")\n                .execute(new SimpleCallBack\u003cSkinTestResult\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onSuccess(SkinTestResult response) {\n                        showToast(response.toString());\n                    }\n                });\n\n        //在需要取消网络请求的地方调用,一般在onDestroy()中\n        //EasyHttp.cancelSubscription(disposable);\n```\n### 带有进度框的请求\n带有进度框的请求，可以设置对话框消失是否自动取消网络和自定义对话框功能，具体参数作用请看请求回调讲解\n#### 方式一：ProgressDialogCallBack\nProgressDialogCallBack带有进度框的请求，可以设置对话框消失是否自动取消网络和自定义对话框功能，具体参数作用请看自定义CallBack讲解\n```\n IProgressDialog mProgressDialog = new IProgressDialog() {\n            @Override\n            public Dialog getDialog() {\n                ProgressDialog dialog = new ProgressDialog(MainActivity.this);\n                dialog.setMessage(\"请稍候...\");\n                return dialog;\n            }\n        };\n        EasyHttp.get(\"/v1/app/chairdressing/\")\n                .params(\"param1\", \"paramValue1\")\n                .execute(new ProgressDialogCallBack\u003cSkinTestResult\u003e(mProgressDialog, true, true) {\n                    @Override\n                    public void onError(ApiException e) {\n                        super.onError(e);//super.onError(e)必须写不能删掉或者忘记了\n                        //请求失败\n                    }\n\n                    @Override\n                    public void onSuccess(SkinTestResult response) {\n                       //请求成功\n                    }\n                });\n```\n*注：错误回调 super.onError(e);必须写*\n#### 方式二：ProgressSubscriber\n\n```\nIProgressDialog mProgressDialog = new IProgressDialog() {\n            @Override\n            public Dialog getDialog() {\n                ProgressDialog dialog = new ProgressDialog(MainActivity.this);\n                dialog.setMessage(\"请稍候...\");\n                return dialog;\n            }\n        };\n EasyHttp.get(URL)\n                .timeStamp(true)\n                .execute(SkinTestResult.class)\n                .subscribe(new ProgressSubscriber\u003cSkinTestResult\u003e(this, mProgressDialog) {\n                    @Override\n                    public void onError(ApiException e) {\n                        super.onError(e);\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onNext(SkinTestResult skinTestResult) {\n                        showToast(skinTestResult.toString());\n                    }\n                });\n\n```\n\n### 请求返回Observable\n通过网络请求可以返回Observable，这样就可以很好的通过Rxjava与其它场景业务结合处理，甚至可以通过Rxjava的connect()操作符处理多个网络请求。例如：在一个页面有多个网络请求，如何在多个请求都访问成功后再显示页面呢？这也是Rxjava强大之处。\n*注：目前通过execute(Class\u003cT\u003e clazz)方式只支持标注的ApiResult结构，不支持自定义的ApiResult*\n示例：\n```\nObservable\u003cSkinTestResult\u003e observable = EasyHttp.get(url)\n                .params(\"param1\", \"paramValue1\")\n                .execute(SkinTestResult.class);\n\n        observable.subscribe(new BaseSubscriber\u003cSkinTestResult\u003e() {\n            @Override\n            public void onError(ApiException e) {\n                showToast(e.getMessage());\n            }\n\n            @Override\n            public void onNext(SkinTestResult skinTestResult) {\n                showToast(skinTestResult.toString());\n            }\n        });\n```\n\n### 文件下载\n本库提供的文件下载非常简单，没有提供复杂的下载方式例如：下载管理器、断点续传、多线程下载等，因为不想把本库做重。如果复杂的下载方式，还请考虑其它下载方案。\n文件目录如果不指定,默认下载的目录为/storage/emulated/0/Android/data/包名/files\n文件名如果不指定,则按照以下规则命名:\n\u003e1.首先检查用户是否传入了文件名,如果传入,将以用户传入的文件名命名\n\u003e2.如果没有传入文件名，默认名字是时间戳生成的。\n\u003e3.如果传入了文件名但是没有后缀，程序会自动解析类型追加后缀名\n\n示例：\n```\n String url = \"http://61.144.207.146:8081/b8154d3d-4166-4561-ad8d-7188a96eb195/2005/07/6c/076ce42f-3a78-4b5b-9aae-3c2959b7b1ba/kfid/2475751/qqlite_3.5.0.660_android_r108360_GuanWang_537047121_release_10000484.apk\";\n        EasyHttp.downLoad(url)\n                .savePath(\"/sdcard/test/QQ\")\n                .saveName(\"release_10000484.apk\")//不设置默认名字是时间戳生成的\n                .execute(new DownloadProgressCallBack\u003cString\u003e() {\n                    @Override\n                    public void update(long bytesRead, long contentLength, boolean done) {\n                        int progress = (int) (bytesRead * 100 / contentLength);\n                        HttpLog.e(progress + \"% \");\n                        dialog.setProgress(progress);\n                        if (done) {//下载完成\n                        }\n                        ...\n                    }\n\n                    @Override\n                    public void onStart() {\n                       //开始下载\n                    }\n\n                    @Override\n                    public void onComplete(String path) {\n                       //下载完成，path：下载文件保存的完整路径\n                    }\n\n                    @Override\n                    public void onError(ApiException e) {\n                        //下载失败\n                    }\n                });\n```\n\n### POST请求，上传String、json、object、body、byte[]\n一般此种用法用于与服务器约定的数据格式，当使用该方法时，params中的参数设置是无效的，所有参数均需要通过需要上传的文本中指定，此外，额外指定的header参数仍然保持有效。\n- `.upString(\"这是要上传的长文本数据！\")//默认类型是：MediaType.parse(\"text/plain\")`\n- 如果你对请求头有自己的要求，可以使用这个重载的形式，传入自定义的content-type文本\n `upString(\"这是要上传的长文本数据！\", \"application/xml\") // 比如上传xml数据，这里就可以自己指定请求头`\n- upJson该方法与upString没有本质区别，只是数据格式是json,通常需要自己创建一个实体bean或者一个map，把需要的参数设置进去，然后通过三方的Gson或者 fastjson转换成json字符串，最后直接使用该方法提交到服务器。\n`.upJson(jsonObject.toString())//上传json`\n- `.upBytes(new byte[]{})//上传byte[]`\n- `.requestBody(body)//上传自定义RequestBody`\n- `.upObject(object)//上传对象object`   必须要增加`.addConverterFactory(GsonConverterFactory.create())`设置\n\n\n\u003e 1.upString、upJson、requestBody、upBytes、upObject五个方法不能同时使用，当前只能选用一个\n\u003e 2.使用upJson、upObject时候params、sign(true/false)、accessToken（true/false）、拦截器都不会起作用\n\n\n示例：\n``` \nHashMap\u003cString, String\u003e params = new HashMap\u003c\u003e();\nparams.put(\"key1\", \"value1\");\nparams.put(\"key2\", \"这里是需要提交的json格式数据\");\nparams.put(\"key3\", \"也可以使用三方工具将对象转成json字符串\");\nJSONObject jsonObject = new JSONObject(params);\n\nRequestBody body=RequestBody.create(MediaType.parse(\"xxx/xx\"),\"内容\");\nEasyHttp.post(\"v1/app/chairdressing/news/favorite\")\n                //.params(\"param1\", \"paramValue1\")//不能使用params，upString 与 params 是互斥的，只有 upString 的数据会被上传\n                .upString(\"这里是要上传的文本！\")//默认类型是：MediaType.parse(\"text/plain\")\n                //.upString(\"这是要上传的长文本数据！\", \"application/xml\") // 比如上传xml数据，这里就可以自己指定请求头\n                \n                 //.upJson(jsonObject.toString())\n                 //.requestBody(body)\n                 //.upBytes(new byte[]{})\n                 //.upObject(object)\n                .execute(new SimpleCallBack\u003cString\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onSuccess(String response) {\n                        showToast(response);\n                    }\n                });\n\n```\n\n### 上传图片或者文件\n\u003e支持单文件上传、多文件上传、混合上传，同时支持进度回调，\n\u003e暂不实现多线程上传/分片上传/断点续传等高级功能\n\n上传文件支持文件与参数一起同时上传，也支持一个key上传多个文件，以下方式可以任选\n上传文件支持两种进度回调：ProgressResponseCallBack(线程中回调)和UIProgressResponseCallBack（可以刷新UI）\n```\nfinal UIProgressResponseCallBack listener = new UIProgressResponseCallBack() {\n            @Override\n            public void onUIResponseProgress(long bytesRead, long contentLength, boolean done) {\n                int progress = (int) (bytesRead * 100 / contentLength);\n                if (done) {//完成\n                }\n                ...\n            }\n        };\n        EasyHttp.post(\"/v1/user/uploadAvatar\")\n                //支持上传新增的参数\n                //.params(String key, File file, ProgressResponseCallBack responseCallBack)\n                //.params(String key, InputStream stream, String fileName, ProgressResponseCallBack responseCallBack)\n                //.params(String key, byte[] bytes, String fileName, ProgressResponseCallBack responseCallBack) \n                //.addFileParams(String key, List\u003cFile\u003e files, ProgressResponseCallBack responseCallBack)\n                //.addFileWrapperParams(String key, List\u003cHttpParams.FileWrapper\u003e fileWrappers)\n                //.params(String key, File file, String fileName, ProgressResponseCallBack responseCallBack)\n                //.params(String key, T file, String fileName, MediaType contentType, ProgressResponseCallBack responseCallBack)\n                \n                //方式一：文件上传\n                File file = new File(\"/sdcard/1.jpg\");\n                //如果有文件名字可以不用再传Type,会自动解析到是image/*\n                .params(\"avatar\", file, file.getName(), listener)\n                //.params(\"avatar\", file, file.getName(),MediaType.parse(\"image/*\"), listener)\n\n                //方式二：InputStream上传\n               final InputStream inputStream = getResources().getAssets().open(\"1.jpg\");\n                .params(\"avatar\", inputStream, \"test.png\", listener)\n                \n                //方式三：byte[]上传\n                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);\n                ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);\n                final byte[] bytes = baos.toByteArray();\n                //.params(\"avatar\",bytes,\"streamfile.png\",MediaType.parse(\"image/*\"),listener)\n                //如果有文件名字可以不用再传Type,会自动解析到是image/*\n                .params(\"avatar\", bytes, \"streamfile.png\", listener)\n        \n                .params(\"file1\", new File(\"filepath1\"))   // 可以添加文件上传\n\t            .params(\"file2\", new File(\"filepath2\")) \t// 支持多文件同时添加上传\n\t            .addFileParams(\"key\", List\u003cFile\u003e files)\t// 这里支持一个key传多个文件\n                .params(\"param1\", \"paramValue1\") \t\t// 这里可以上传参数\n                .accessToken(true)\n                .timeStamp(true)\n                .execute(new ProgressDialogCallBack\u003cString\u003e(mProgressDialog, true, true) {\n                    @Override\n                    public void onError(ApiException e) {\n                        super.onError(e);\n                        showToast(e.getMessage());\n                    }\n\n                    @Override\n                    public void onSuccess(String response) {\n                        showToast(response);\n                    }\n                });\n```\n### 取消请求\n#### 通过Disposable取消\n每个请求前都会返回一个Disposable，取消订阅就可以取消网络请求，如果是带有进度框的网络请求，则不需要手动取消网络请求，会自动取消。\n```\n Disposable mSubscription = EasyHttp.get(url).execute(callback);\n  ...\n  @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        EasyHttp.cancelSubscription(mSubscription);\n    }\n```\n#### 通过dialog取消\n自动取消使用ProgressDialogCallBack回调或者使用ProgressSubscriber,就不用再手动调用cancelSubscription();\nProgressDialogCallBack:\n ```\nEasyHttp.get(url).execute(new ProgressDialogCallBack());\n```\nProgressSubscriber\n```\nEasyHttp.get(url).execute(SkinTestResult.class).subscribe(new ProgressSubscriber\u003cSkinTestResult\u003e(this, mProgressDialog) {\n            @Override\n            public void onError(ApiException e) {\n                super.onError(e);\n                showToast(e.getMessage());\n            }\n\n            @Override\n            public void onNext(SkinTestResult skinTestResult) {\n                showToast(skinTestResult.toString());\n            }\n        })\n```\n\n### 同步请求\n同步请求只需要设置syncRequest()方法\n```\n EasyHttp.get(\"/v1/app/chairdressing/skinAnalyzePower/skinTestResult\")\n                ...\n                .syncRequest(true)//设置同步请求\n                .execute(new CallBack\u003cSkinTestResult\u003e() {});\n```\n\n### 请求回调CallBack支持的类型\n```\n//支持回调的类型可以是Bean、String、CacheResult\u003cBean\u003e、CacheResult\u003cString\u003e、List\u003cBean\u003e\nnew SimpleCallBack\u003cCacheResult\u003cBean\u003e\u003e()//支持缓存的回调，请看缓存讲解\nnew SimpleCallBack\u003cCacheResult\u003cString\u003e\u003e()//支持缓存的回调，请看缓存讲解\nnew SimpleCallBack\u003cBean\u003e()//返回Bean\nnew SimpleCallBack\u003cString\u003e()//返回字符串\nnew SimpleCallBack\u003cList\u003cBean\u003e()//返回集合\n```\n*注：其它回调同理*\n\n### cookie使用\ncookie的内容主要包括：名字，值，过期时间，路径和域。路径与域一起构成cookie的作用范围,关于cookie的作用这里就不再科普，自己可以去了解\ncookie设置：\n```\nEasyHttp.getInstance()\n   \t\t\t\t ...\n                  //如果不想让本库管理cookie,以下不需要\n                .setCookieStore(new CookieManger(this)) //cookie持久化存储，如果cookie不过期，则一直有效\n                 ...\n```\n\n- 查看url所对应的cookie\n\n```\nHttpUrl httpUrl = HttpUrl.parse(\"http://www.xxx.com/test\");\nCookieManger cookieManger = getCookieJar();\nList\u003cCookie\u003e cookies =  cookieManger.loadForRequest(httpUrl);\n```\n\n- 查看CookieManger所有cookie\n \n```\nPersistentCookieStore cookieStore= getCookieJar().getCookieStore();\nList\u003cCookie\u003e cookies1= cookieStore.getCookies();\n```\n\n- 添加cookie\n\n```\nCookie.Builder builder = new Cookie.Builder();\nCookie cookie = builder.name(\"mCookieKey1\").value(\"mCookieValue1\").domain(httpUrl.host()).build();\nCookieManger cookieManger = getCookieJar();\ncookieManger.saveFromResponse(httpUrl, cookie);\n//cookieStore.saveFromResponse(httpUrl, cookieList);//添加cookie集合\n```\n\n- 移除cookie\n\n```\nHttpUrl httpUrl = HttpUrl.parse(\"http://www.xxx.com/test\");\nCookieManger cookieManger = EasyHttp.getCookieJar();\nCookie cookie = builder.name(\"mCookieKey1\").value(\"mCookieValue1\").domain(httpUrl.host()).build();\ncookieManger.remove(httpUrl,cookie);\n```\n\n- 清空cookie\n\n```\nCookieManger cookieManger = EasyHttp.getCookieJar();\ncookieManger.removeAll();\n```\n\n### 自定义call()请求\n提供了用户自定义ApiService的接口，您只需调用call方法即可.\n示例：\n```\npublic interface LoginService {\n    @POST(\"{path}\")\n    @FormUrlEncoded\n    Observable\u003cApiResult\u003cAuthModel\u003e\u003e login(@Path(\"path\") String path, @FieldMap Map\u003cString, String\u003e map);\n}\n\nfinal CustomRequest request = EasyHttp.custom()\n                .addConverterFactory(GsonConverterFactory.create(new Gson()))//自定义的可以设置GsonConverterFactory\n                .params(\"param1\", \"paramValue1\")\n                .build();\n\n        LoginService mLoginService = request.create(LoginService.class);\n        LoginService mLoginService = request.create(LoginService.class);\n        Observable\u003cApiResult\u003cAuthModel\u003e\u003e observable = request.call(mLoginService.login(\"v1/account/login\", request.getParams().urlParamsMap));\n        Disposable subscription = observable.subscribe(new Action1\u003cApiResult\u003cAuthModel\u003e\u003e() {\n            @Override\n            public void call(ApiResult\u003cAuthModel\u003e result) {\n                //请求成功\n            }\n        }, new Action1\u003cThrowable\u003e() {\n            @Override\n            public void call(Throwable throwable) {\n                //请求失败\n            }\n        });\n```\n\n### 自定义apiCall()请求\n提供默认的支持ApiResult结构，数据返回不需要带ApiResult,直接返回目标.\n示例：\n```\nObservable\u003cAuthModel\u003e observable = request.apiCall(mLoginService.login(\"v1/account/login\", request.getParams().urlParamsMap));\n```\n## 请求回调和订阅\n请求回调本库提供两种方式Callback和Subscriber\n### 回调方式\n此种方式主要针对execute(CallBack\u003cT\u003e callBack)，目前内部提供的回调包含CallBack, SimpleCallBack ,ProgressDialogCallBack ,DownloadProgressCallBack 可以根据自己的需求去自定义Callback\n\n- CallBack所有回调的基类，抽象类\n- SimpleCallBack简单回调，只有成功和失败\n- ProgressDialogCallBack带有进度框的回调，可以自定义进度框、支持是否可以取消对话框、对话框消失自动取消网络请求等参数设置\n- DownloadProgressCallBack如果要做文件下载，则必须使用该回调，内部封装了关于文件下载进度回调的方法，如果使用其他回调也可以，但是没有进度通知\n\n该网络框架的核心使用方法即为Callback的继承使用，因为不同的项目需求，会有个性化的回调请自定义\n#### CallBack回调\n```\nnew CallBack\u003cT\u003e() {\n                    @Override\n                    public void onStart() {\n                       //请求开始\n                    }\n\n                    @Override\n                    public void onCompleted() {\n                       //请求完成\n                    }\n\n                    @Override\n                    public void onError(ApiException e) {\n                       //请求失败\n                    }\n\n                    @Override\n                    public void onSuccess(T t) {\n                       //请求成功\n                    }\n                }\n```\n\n#### SimpleCallBack回调\n\n```\nnew SimpleCallBack\u003cT\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                         //请求失败\n                    }\n\n                    @Override\n                    public void onSuccess(T t) {\n                        //请求成功\n                    }\n                }\n```\n\n#### ProgressDialogCallBack回调\n可以自定义带有加载进度框的回调，取消对话框会自动取消掉网络请求\n\n提供两个构造\n\u003e public ProgressDialogCallBack(IProgressDialog progressDialog);//默认不能取消对话框\n\u003e public ProgressDialogCallBack(IProgressDialog progressDialog, boolean isShowProgress, boolean isCancel);//自定义加载进度框,可以设置是否显示弹出框，是否可以取消 progressDialog: dialog对象接口  isShowProgress：对话框消失是否取消网络请求 isCancel：是否可以取消对话框对应Dialog的setCancelable(isCancel)方法;\n\n自定义ProgressDialog对话框\n```\n private IProgressDialog mProgressDialog = new IProgressDialog() {\n        @Override\n        public Dialog getDialog() {\n            ProgressDialog dialog = new ProgressDialog(MainActivity.this);\n            dialog.setMessage(\"请稍候...\");\n            return dialog;\n        }\n    };\n```\n#### DownloadProgressCallBack回调\n此回调只用于文件下载，具体请看文件下载讲解\n#### 自定义CallBack回调\n如果对回调有特殊需求，支持可以继承CallBack自己扩展功能\n\n### 订阅方式\n此种方式主要是针对execute(Class\u003cT\u003e clazz)和execute(Type type)，目前内部提供的Subscriber包含BaseSubscriber、DownloadSubscriber、ProgressSubscriber，可以根据自己的需求去自定义Subscriber\n- BaseSubscriber所有订阅者的基类，抽象类\n- DownloadSubscriber下载的订阅者，上层不需要关注\n- ProgressSubscriber带有进度框的订阅，可以自定义进度框、支持是否可以取消对话框、对话框消失自动取消网络请求等参数设置\n\n```\nnew BaseSubscriber\u003cT\u003e() {\n            @Override\n            public void onError(ApiException e) {\n               //请求失败\n            }\n\n            @Override\n            public void onNext(T t) {\n                //请求成功\n            }\n        }\n```\n\n```\nnew ProgressSubscriber\u003cT\u003e(this, mProgressDialog) {\n                    @Override\n                    public void onError(ApiException e) {\n                        super.onError(e);\n                        //请求失败\n                    }\n\n                    @Override\n                    public void onNext(T t) {\n                         //请求成功\n                    }\n                }\n```\n\n### 自定义Subscriber\n如果对Subscriber有特殊需求，支持可以继承BaseSubscriber自己扩展订阅者\n## 缓存使用\n### 缓存介绍\n本库的缓存主要分okhttp的Cache缓存和自定义的RxCache缓存,大家有疑问okhttp有缓存，retrofit也是支持通过header来设置缓存，为什么还要自定义一个缓存机制呢？通过自定义RxCache缓存使用更简单，更符合我们常用的业务需求(常用的缓存策略也不会太复杂)， retrofit的缓存借助于okhttp通过拦截器interceptor实现或者通过[`@Headers(\"Cache-Control: public, max-age=3600)`](http://www.jianshu.com/p/9c3b4ea108a7)具体用法这里不做详细描述，有兴趣的可以自己去了解。动态修改缓存时间不方便，例如：同一个接口，不同时间段请求的内容缓存的时间不一样，需要动态修改。\n\n对于`DEFAULT`模式是okhttp的Cache缓存。因为该模式是完全遵循标准的http协议的,缓存时间是依靠服务端响应头来控制，也可以通过拦截器自己处理\n\n对于RxCache的缓存支持多种存储方式，提供`IDiskConverter`转换器接口目前支持`SerializableDiskConverter`和`GsonDiskConverter`两种方式，也可以自定义Parcelable、fastjson、xml、kryo等转换器\n**SerializableDiskConverter**\n使用缓存前，必须让缓存的数据所有javaBean对象实现Serializable接口，否则会报NotSerializableException。 因为缓存的原理是将对象序列化后保存，如果不实现Serializable接口，会导致对象无法序列化，进而无法保存，也就达不到缓存的效果。\n优点：存储和读取都不用再转化直接就是需要的对象速度快 \n缺点：如果javabean里面还有javabean且层级比较多，也必须每个都要实现Serializable接口，比较麻烦\n**GsonDiskConverter**\n此种方式就是以json字符串的方式存储\n优点：相对于SerializableDiskConverter转换器，存储的对象不需要进行序列化\n缺点：就是存储和读取都要使用Gson进行转换，object-\u003eString-\u003eObject的给一个过程，相对来说每次都要转换性能略低，但是性能基本忽略不计\n\n目前提供了八种CacheMode缓存模式,每种缓存模式都可以指定对应的CacheTime,将复杂常用的业务场景封装在里面，让你不用关心缓存的具体实现，而专注于数据的处理\n\n- NO_CACHE：不使用缓存,该模式下,cacheKey,cacheTime 等参数均无效\n- DEFAULT：按照HTTP协议的默认缓存规则，走OKhttp的Cache缓存\n- FIRSTREMOTE：先请求网络，请求网络失败后再加载缓存\n- FIRSTCACHE：先加载缓存，缓存没有再去请求网络\n- ONLYREMOTE：仅加载网络，但数据依然会被缓存\n- ONLYCACHE：只读取缓存，缓存没有会返回null\n- CACHEANDREMOTE:先使用缓存，不管是否存在，仍然请求网络，CallBack会回调两次.\n- CACHEANDREMOTEDISTINCT:先使用缓存，不管是否存在，仍然请求网络，CallBack回调不一定是两次，如果发现请求的网络数据和缓存数据是一样的，就不会再返回网络的回调,既回调一次。否则不相同仍然会回调两次。（目的是为了防止数据没有发生变化，也需要回调两次导致界面无用的重复刷新）,**此种模式缓存的对象bean一定要重写tostring()方法**\n\n*注：无论对于哪种缓存模式，都可以指定一个cacheKey，建议针对不同需要缓存的页面设置不同的cacheKey，如果相同，会导致数据覆盖。*\n\n### 缓存设置\n缓存设置有两种方式\n方式一：全局设置，所有请求都会默认使用此模式\n```\n EasyHttp.getInstance()\n \t\t\t\t...\n                .setCacheMode(CacheMode.CACHEANDREMOTE)//不设置默认是NO_CACHE模式\n                ...\n```\n方式二：单个请求设置缓存模式\n```\n EasyHttp.get(URL)\n \t\t\t\t...\n                .cacheMode(CacheMode.FIRSTREMOTE)\n                ...\n```\n### 设置转换器\n方式一：全局设置，所有请求都会默认使用此存储转换器\n```\nEasyHttp.getInstance().setCacheDiskConverter(new SerializableDiskConverter())//默认缓存使用序列化转化\n```\n方式二：单个请求设置存储转换器\n```\nEasyHttp.get(URL).cacheDiskConverter(new GsonDiskConverter());\n```\n*注：一个请求就选用一种转换器，切记不要使用SerializableDiskConverter来缓存，又用GsonDiskConverter来读会报错*\n\n### 自定义转换器\n如果你想拥有自己的转换器请实现`IDiskConverter`接口。\n示例：\n```\npublic class CustomDiskConverter implements IDiskConverter {\n    @Override\n    public \u003cT\u003e T load(InputStream source, Type type) {\n        //实现读功能\n        return null;\n    }\n\n    @Override\n    public boolean writer(OutputStream sink, Object data) {\n        //实现写功能\n        return false;\n    }\n}\n\n```\n### 缓存回调\n对具有缓存的回调CallBack，如果你想知道当前的缓存是来自本地还是网络，只需要回调中加入CacheResult，其它和普通的网络请求方式一模一样。CacheResult中的isFromCache可以知道是否来自缓存，true：来自缓存，false：来自网络。请使用`new SimpleCallBack\u003cCacheResult\u003cT\u003e\u003e()` 也就是在你原有的T上包含一层CacheResult就可以了。如果不想用到isFromCache就不需要用CacheResult，直接使用`new SimpleCallBack\u003cT\u003e()`\n带有CacheResult回调示例：\n```\n EasyHttp.get(url)\n                .readTimeOut(30 * 1000)//测试局部读超时30s\n                .cacheMode(cacheMode)\n                .cacheKey(this.getClass().getSimpleName())//缓存key\n                .retryCount(5)//重试次数\n                .cacheTime(5 * 60)//缓存时间300s，默认-1永久缓存  okhttp和自定义缓存都起作用\n                //.okCache(new Cache());//okhttp缓存，模式为默认模式（CacheMode.DEFAULT）才生效\n                //.cacheDiskConverter(new GsonDiskConverter())//默认使用的是 new SerializableDiskConverter();\n                .cacheDiskConverter(new SerializableDiskConverter())//默认使用的是 new SerializableDiskConverter();\n                .timeStamp(true)\n                .execute(new SimpleCallBack\u003cCacheResult\u003cSkinTestResult\u003e\u003e() {\n\n                    @Override\n                    public void onError(ApiException e) {\n                       //请求失败\n                    }\n\n                    @Override\n                    public void onSuccess(CacheResult\u003cSkinTestResult\u003e cacheResult) {\n                        HttpLog.i(cacheResult.toString());\n                        String from = \"\";\n                        if (cacheResult.isFromCache) {\n                            from = \"我来自缓存\";\n                        } else {\n                            from = \"我来自远程网络\";\n                        }\n                       ....\n                    }\n                });\n```\n### 移除缓存\n支持根据缓存key移除缓存，主要是针对RxCache才能起作用\n```\nEasyHttp.removeCache(\"cachekey\");\n```\n### 清空缓存\n```\nEasyHttp.clearCache();\n```\n\n### RxCache\nRxCache是自己封装的一个本地缓存功能库，采用Rxjava+DiskLruCache来实现，线程安全内部采用ReadWriteLock机制防止频繁读写缓存造成的异常，可以独立使用，单独用RxCache来存储数据。采用transformer与网络请求结合，可以实现网络缓存功能,本地硬缓存，具有缓存读写功能（异步）、缓存是否存在、根据key删除缓存、清空缓存（异步）、缓存Key会自动进行MD5加密、可以设置缓存磁盘大小、缓存key、缓存时间、缓存存储的转换器、缓存目录、缓存Version等功能本库不作为重点介绍。后期会将此代码独立开源一个库，作为一分钟让你自己的网络库也具有缓存功能，敬请期待！！！\n\n## 动态参数\n动态参数就是像我们的token、时间戳timeStamp、签名sign等，这些参数不能是全局参数因为是变化的，设置成局部参数又太麻烦，每次都要获取。token是有有效时间的或者异地登录等都会变化重新获取，时间戳一般是根据系统的时间，sign是根据请求的url和参数进行加密签名一般都有自己的签名规则。有的接口需要这些参数有的接口不需要，本库很好的解决这个问题。\n#### 1.在请求的时候可以设置下面三个参数\n```\n.accessToken(true)//本次请求是否追加token\n.timeStamp(false)//本次请求是否携带时间戳\n.sign(false)//本次请求是否需要签名\n```\n#### 2.需要继承库中提供的动态拦截器BaseDynamicInterceptor\n继承BaseDynamicInterceptor后就可以获取到参数的设置值，请详细看`CustomSignInterceptor`的注释讲解，也可以查看Demo示例\n示例:\n```\n/**\n * \u003cp\u003e描述：对参数进行签名、添加token、时间戳处理的拦截器\u003c/p\u003e\n * 主要功能说明：\u003cbr\u003e\n * 因为参数签名没办法统一，签名的规则不一样，签名加密的方式也不同有MD5、BASE64等等，只提供自己能够扩展的能力。\u003cbr\u003e\n * 作者： zhouyou\u003cbr\u003e\n * 日期： 2017/5/4 15:21 \u003cbr\u003e\n * 版本： v1.0\u003cbr\u003e\n */\npublic class CustomSignInterceptor extends BaseDynamicInterceptor\u003cCustomSignInterceptor\u003e {\n    @Override\n    public TreeMap\u003cString, String\u003e dynamic(TreeMap\u003cString, String\u003e dynamicMap) {\n        //dynamicMap:是原有的全局参数+局部参数\n        //你不必关心当前是get/post/上传文件/混合上传等，库中会自动帮你处理。\n        //根据需要自己处理，如果你只用到token则不必处理isTimeStamp()、isSign()\n        if (isTimeStamp()) {//是否添加时间戳，因为你的字段key可能不是timestamp,这种动态的自己处理\n            dynamicMap.put(ComParamContact.Common.TIMESTAMP, String.valueOf(System.currentTimeMillis()));\n        }\n        if (isSign()) {是否签名\n            //1.因为你的字段key可能不是sign，这种需要动态的自己处理\n            //2.因为你的签名的规则不一样，签名加密方式也不一样，只提供自己能够扩展的能力\n            dynamicMap.put(ComParamContact.Common.SIGN, sign(dynamicMap));\n        }\n        if (isAccessToken()) {//是否添加token\n            String acccess = TokenManager.getInstance().getAuthModel().getAccessToken();\n            dynamicMap.put(ComParamContact.Common.ACCESSTOKEN, acccess);\n        }\n        //Logc.i(\"dynamicMap:\" + dynamicMap.toString());\n        return dynamicMap;//dynamicMap:是原有的全局参数+局部参数+新增的动态参数\n    }\n\n    //示例-\u003e签名规则：POST+url+参数的拼装+secret\n    private String sign(TreeMap\u003cString, String\u003e dynamicMap) {\n        String url = getHttpUrl().url().toString();\n        url = url.replaceAll(\"%2F\", \"/\");\n        StringBuilder sb = new StringBuilder(\"POST\");\n        sb.append(url);\n        for (Map.Entry\u003cString, String\u003e entry : dynamicMap.entrySet()) {\n            sb.append(entry.getKey()).append(\"=\").append(entry.getValue()).append(\"\u0026\");\n        }\n\n        sb.append(AppConstant.APP_SECRET);\n        HttpLog.i(sb.toString());\n        return MD5.encode(sb.toString());\n    }\n}\n```\n#### 3.设置自定义的动态拦截器\n最好通过全局的方式设置，因为一般很多接口都会使用到\n```\n EasyHttp.getInstance()\n                 ...\n                .addInterceptor(new CustomSignInterceptor())//添加动态参数（签名、token、时间戳）拦截器\n                 ...\n```\n## 自定义ApiResult\n本库中默认提供的是标准ApiResult.内部是靠ApiResult进行解析的，如果你的数据结构跟ApiResult不同，你可以在你的项目中继承ApiResult，然后重写getCode()、getData()、getMsg()和isOk()等方法来实现自己的需求。\n本库中ApiResult如下：\n```\npublic class ApiResult\u003cT\u003e {\n    private int code;\n    private String msg;\n    private T data;\n    public int getCode() {\n        return code;\n    }\n\n    public void setCode(int code) {\n        this.code = code;\n    }\n\n    public String getMsg() {\n        return msg;\n    }\n\n    public void setMsg(String msg) {\n        this.msg = msg;\n    }\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    public boolean isOk() {//请求成功的判断方法\n        return code == 0 ? true : false;\n    }\n}\n```\njson格式类似:\n```\n{\n\"code\": 100010101,\n\"data\": 内容,\n\"msg\": \"请求成功\"\n}\n```\n假如你的数据结构是这样的：\n```\n{\n\"error_code\": 0,\n\"result\": 内容,\n\"reason\": \"请求成功\"\n}\n```\n那么你的basebean可以写成这样\n```\npublic class CustomApiResult\u003cT\u003e extends ApiResult\u003cT\u003e {\n    String reason;\n    int error_code;\n    //int resultcode;\n    T result;\n    @Override\n    public int getCode() {\n        return error_code;\n    }\n    @Override\n    public void setCode(int code) {\n        error_code = code;\n    }\n    @Override\n    public String getMsg() {\n        return reason;\n    }\n    @Override\n    public void setMsg(String msg) {\n        reason = msg;\n    }\n    @Override\n    public T getData() {\n        return result;\n    }\n    @Override\n    public void setData(T data) {\n        result = data;\n    }\n   /* @Override\n    public boolean isOk() {\n        return error_code==200;//如果不是0表示成功，请重写isOk()方法。\n    }*/\n}\n```\n那么你的网络请求可以这样写\n##### 自定义ApiResult回调方式（通过CallBackProxy代理）\n```\nEasyHttp.get(url)\n                .readTimeOut(30 * 1000)//局部定义读超时\n                .writeTimeOut(30 * 1000)\n                .connectTimeout(30 * 1000)\n                //.cacheKey(this.getClass().getSimpleName()+\"11\")\n                //.cacheMode(CacheMode.CACHEANDREMOTE)\n                //.cacheMode(CacheMode.ONLYREMOTE)\n                //.headers(\"\",\"\")//设置头参数\n                //.params(\"name\",\"张三\")//设置参数\n                //.addInterceptor()\n                //.addConverterFactory()\n                //.addCookie()\n                //.timeStamp(true)\n                .baseUrl(\"http://apis.juhe.cn\")\n                .params(\"phone\", \"手机号\")\n                .params(\"dtype\", \"json\")\n                .params(\"key\", \"5682c1f44a7f486e40f9720d6c97ffe4\")\n                .execute(new CallBackProxy\u003cCustomApiResult\u003cResultBean\u003e, ResultBean\u003e(new SimpleCallBack\u003cResultBean\u003e() {\n                    @Override\n                    public void onError(ApiException e) {\n                        //请求错误\n                    }\n\n                    @Override\n                    public void onSuccess(ResultBean response) {\n                        //请求成功\n                    }\n                }) {\n                });\n```\n\n这种写法会觉得有点长，CallBackProxy的泛型参数每次都需要填写，其中CustomApiResult是继承ApiResult的，CustomApiResult相当于项目的basebean,对于一个实际项目来讲，basebean是固定的，所以我们可以继续封装这个方法，根据需要一般只需要封装get和post请求就可以了。\n```\n public static \u003cT\u003e Disposable customExecute(CallBack\u003cT\u003e callBack) {\n        return execute(new CallBackProxy\u003cCustomApiResult\u003cT\u003e, T\u003e(callBack) {\n        });\n    }\n```\n\n通过以上改造，再次调用时直接使用CallBack，不用再关注CallBackProxy，是不是明显简单很多了，具体请看代码Demo!!!\n##### 自定义ApiResult订阅方式（通过CallClazzProxy代理）\n```\nObservable\u003cResultBean\u003e observable = EasyHttp.get(\"/mobile/get\")\n                .readTimeOut(30 * 1000)//局部定义读超时\n                .writeTimeOut(30 * 1000)\n                .baseUrl(\"http://apis.juhe.cn\")\n                .params(\"phone\", \"18688994275\")\n                .params(\"dtype\", \"json\")\n                .params(\"key\", \"5682c1f44a7f486e40f9720d6c97ffe4\")\n                .execute(new CallClazzProxy\u003cCustomApiResult\u003cResultBean\u003e, ResultBean\u003e(ResultBean.class) {\n                });\n        observable.subscribe(new ProgressSubscriber\u003cResultBean\u003e(this, mProgressDialog) {\n            @Override\n            public void onError(ApiException e) {\n                super.onError(e);\n                showToast(e.getMessage());\n            }\n\n            @Override\n            public void onNext(ResultBean result) {\n                showToast(result.toString());\n            }\n        });\n```\n## 调试模式\n一个好的库，一定有比较人性化的调试模式，为了方便开发者查看请求过程和请求日志，本库提供详细的日志打印，最好在开发阶段，请打开调试模式输出优雅的Log.\n调试模式的控制在初始化配置时就可以直接设置。\n```\npublic class MyApplication extends Application {\n        @Override\n        public void onCreate() {\n            super.onCreate();\n            ...\n            EasyHttp.getInstance()\n            \t\t...\n                    // 打开该调试开关并设置TAG,不需要就不要加入该行\n                    // 最后的true表示是否打印内部异常，一般打开方便调试错误\n                    .debug(\"EasyHttp\", true);\n        }\n    }\n```\n#### Log预览说明\n这里一个成功请求的例子：\n![](http://img.blog.csdn.net/20170608191720172)\n上方的Log打印了一个Request完整的声明周期，一个请求的Log有以下特点：\n1.开头和结尾打了--\u003ehttp is start和 --\u003ehttp is Complete分割请求，完整的生命周期的内容都会打印在开头和结尾的里面。\n2.request请求和response响应分割，分别是\n\u003e -------------------------------request-------------------------------\n\n\u003e -------------------------------response-------------------------------\n\n3.在---request---之后会打印请求的url、当前请求的类型GET/POST... --\u003eGET/POST开头  --\u003eEND GET/POST结尾。如果是GET、HEAD请求方式添加的参数将会在这里完整的以url?key=value\u0026key=value的形式打印。\n4.在----response----之后会打印（在服务器响应后被打印），包含响应码、响应状态、响应头、cookie,body等以\u003c--200(响应码)开头，\u003c--END HTTP结尾\n5.loadCache  key=如果设置了缓存，会看到缓存的key，开启了网络缓存功能才会输出。\n6.loadCache result=从缓存里读取的结果，开启了网络缓存功能才会输出。\n7.save status =\u003e true保存缓存的状态\n## 混淆\n```\n#okhttp\n-dontwarn com.squareup.okhttp3.**\n-keep class com.squareup.okhttp3.** { *;}\n-dontwarn okio.**\n\n# Retrofit\n-dontwarn retrofit2.**\n-keep class retrofit2.** { *; }\n-keepattributes Exceptions\n\n# Retrolambda\n-dontwarn java.lang.invoke.*\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###rxandroid-1.2.1\n-keepclassmembers class rx.android.**{*;}\n\n# Gson\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#RxEasyHttp\n-keep class com.zhouyou.http.model.** {*;}\n-keep class com.zhouyou.http.cache.model.** {*;}\n-keep class com.zhouyou.http.cache.stategy.**{*;}\n```\n[请查看Demo中完整的混淆文件](https://github.com/zhou-you/RxEasyHttp/blob/master/app/proguard-rules.pro)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhou-you%2Frxeasyhttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhou-you%2Frxeasyhttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhou-you%2Frxeasyhttp/lists"}