HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的 异步预处理器 ,特色的 标签 ,灵活的上传下载 进度监听 与 过程控制 功能,在轻松解决很多原本令人头疼问题的同时,设计上也力求纯粹与优雅。
项目地址
Gitee:https://gitee.com/ejlchina-zhxu/okhttps
GitHub:https://github.com/ejlchina/okhttps
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>2.2.0</version>
</dependency>
compile 'com.ejlchina:okhttps:2.2.0'
准备好,我要正式开讲啦!!!
HTTP http = HTTP.builder().build();
以上代码构建了一个最简单的HTTP
实例,它拥有以下三个方法:
async(String url)
开始一个异步请求sync(String url)
开始一个同步请求cancel(String tag)
根据标签批量取消请求为了使用方便,在构建的时候,我们更愿意指定一个BaseUrl
(请参见下文[5.1 设置 BaseUrl]):
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com")
.build();
为了简化文档,下文中出现的http
均是在构建时设置了BaseUrl
的HTTP
实例。
使用方法sync(String url)
开始一个同步请求:
List<User> users = http.sync("/users") // http://api.demo.com/users
.get() // GET请求
.getBody() // 获取响应报文体
.toList(User.class); // 得到目标数据
方法sync
返回一个同步HttpTask
,可链式使用。
使用方法async(String url)
开始一个异步请求:
http.async("/users/1") // http://api.demo.com/users/1
.setOnResponse((HttpResult result) -> {
// 得到目标数据
User user = result.getBody().toBean(User.class);
})
.get(); // GET请求
方法async
返回一个异步HttpTask
,可链式使用。
同步与异步的HttpTask
都拥有get
、post
、put
与delete
方法。不同的是:同步HttpTask
的这些方法返回一个HttpResult
,而异步HttpTask
的这些方法返回一个HttpCall
。
HttpResult res1 = http.sync("/users").get(); // 同步 GET
HttpResult res2 = http.sync("/users")post(); // 同步 POST
HttpResult res3 = http.sync("/users/1").put(); // 同步 PUT
HttpResult res4 = http.sync("/users/1").delete();// 同步 DELETE
HttpCall call1 = http.async("/users").get(); // 异步 GET
HttpCall call2 = http.async("/users").post(); // 异步 POST
HttpCall call3 = http.async("/users/1").put(); // 异步 PUT
HttpCall call4 = http.async("/users/1").delete();// 异步 DELETE
只有异步请求才可以设置回调函数:
http.async("/users/{id}") // http://api.demo.com/users/1
.addPathParam("id", 1)
.setOnResponse((HttpResult result) -> {
// 响应回调
})
.setOnException((Exception e) -> {
// 异常回调
})
.setOnComplete((State state) -> {
// 完成回调,无论成功失败都会执行
})
.get();
HttpResult
是HTTP请求执行完后的结果,它是同步请求方法( get
、post
、put
、delete
)的返回值,也是异步请求响应回调(OnResponse
)的参数,它定义了如下方法:
getState()
得到请求执行状态枚举,它有以下取值:
State.CANCELED
请求被取消State.RESPONSED
已收到响应State.TIMEOUT
请求超时State.NETWORK_ERROR
网络错误State.EXCEPTION
其它请求异常getStatus()
得到HTTP状态码isSuccessful()
是否响应成功,状态码在 [200…300) 之间getHeaders()
得到HTTP响应头getBody()
得到响应报文体Body
实例,它定义了如下方法(对同一个Body
实例,以下的toXXX()
类方法只能使用一个且仅能调用一次):
toBytes()
返回字节数组toByteStream()
返回字节输入流toCharStream()
返回字符输入流toString()
返回字符串toJsonObject()
返回Json对象toJsonArray()
返回Json数组toBean(Class<T> type)
返回根据type自动json解析后的JavaBeantoList(Class<T> type)
返回根据type自动json解析后的JavaBean列表toFile(String filePath)
下载到指定路径toFile(File file)
下载到指定文件toFolder(String dirPath)
下载到指定目录toFolder(File dir)
下载到指定目录getContentType()
返回报文体的媒体类型getContentLength()
返回报文体的字节长度close()
关闭报文体,未对报文体做任何消费时使用,比如只读取报文头getError()
执行中发生的异常,自动捕获执行请求是发生的 网络超时、网络错误 和 其它请求异常close()
关闭报文,未对报文体做任何消费时使用,比如只读取长度示例,请求结果自动转Bean和List:
// 自动转Bean
Order order = http.sync("/orders/1")
.get().getBody().toBean(Order.class);
// 自动转List
List<Order> orders = http.sync("/orders")
.get().getBody().toList(Order.class);
示例,下载文件到指定目录:
String path = "D:/reports/2020-03-01.xlsx"; // 文件保存目录
// 同步下载
http.sync("/reports/2020-03-01.xlsx")
.get().getBody().toFile(path).start();
// 异步下载
http.async("/reports/2020-03-01.xlsx")
.setOnResponse((HttpResult result) -> {
result.getBody().toFile(path).start();
})
.get();
关于上传下载更详细的介绍请看后文:OkHttp 优雅封装 HttpUtils 之 上传下载解密。
HttpCall
对象是异步请求方法(get
、post
、put
、delete
)的返回值,与java
的Future
接口很像,它有如下方法:
cancel()
取消本次请求,返回取消结果isCanceled()
返回请求是否被取消isDone()
返回是否执行完成,包含取消和失败getResult()
返回执行结果HttpResult
对象,若请求未执行完,则挂起当前线程直到执行完成再返回取消一个异步请求示例:
HttpCall call = http.async("/users/1").get();
System.out.println(call.isCanceled()); // false
boolean success = call.cancel(); // 取消请求
System.out.println(success); // true
System.out.println(call.isCanceled()); // true
HTTP
对象的sync
与async
方法返回一个HttpTask
对象,该对象提供了可链式调用的addXXX
与setXXX
系列方法用于构建任务本身。
addHeader(String name, String value)
添加请求头
addHeader(Map<String, String> headers)
添加请求头
addPathParam(String name, Object value)
添加路径参数:替换URL里的{name}占位符
addPathParam(Map<String, ?> params)
添加路径参数:替换URL里的{name}占位符
addUrlParam(String name, Object value)
添加URL参数:拼接在URL的?之后(查询参数)
addUrlParam(Map<String, ?> params)
添加URL参数:拼接在URL的?之后(查询参数)
addBodyParam(String name, Object value)
添加Body参数:以表单key=value&的形式放在报文体内(表单参数)
addBodyParam(Map<String, ?> params)
添加Body参数:以表单key=value&的形式放在报文体内(表单参数)
addJsonParam(String name, Object value)
添加Json参数:请求体为Json(支持多层结构)
addJsonParam(Map<String, ?> params)
添加Json参数:请求体为Json(支持多层结构)
setRequestJson(Object json)
设置请求体的Json字符串 或待转换为 Json的 JavaBean
setRequestJson(Object bean, String dateFormat)
设置请求体的Json字符串 或待转换为 Json的 JavaBean
addFileParam(String name, String filePath)
上传文件
addFileParam(String name, File file)
上传文件
addFileParam(String name, String type, InputStream inputStream)
上传文件
addFileParam(String name, String type, String fileName, InputStream input)
上传文件
addFileParam(String name, String type, byte[] content)
上传文件
addFileParam(String name, String type, String fileName, byte[] content)
上传文件
setTag(String tag)
为HTTP任务添加标签
setRange(long rangeStart)
设置Range头信息,用于断点续传
setRange(long rangeStart, long rangeEnd)
设置Range头信息,用于分块下载
有时候我们想对HTTP任务加以分类,这时候可以使用标签功能:
http.async("/users") //(1)
.setTag("A").get();
http.async("/users") //(2)
.setTag("A.B").get();
http.async("/users") //(3)
.setTag("B").get();
http.async("/users") //(4)
.setTag("B.C").get();
http.async("/users") //(5)
.setTag("C").get();
当使用标签后,就可以按标签批量的对HTTP任务进行取消:
int count = http.cancel("B"); //(2)(3)(4)被取消(取消标签包含"B"的任务)
System.out.println(count); // 输出 3
同样的,只有异步HTTP任务才可以被取消。标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见下文 [并行预处理器] 与 [串行预处理器]。
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com") // 设置 BaseUrl
.build();
配置了BaseUrl
之后,具体的请求便可以省略BaseUrl
部分,使得代码更加简洁,例如:
http.sync("/users").get() // http://api.demo.com/users
http.sync("/auth/signin") // http://api.demo.com/auth/signin
.addBodyParam("username", "Jackson")
.addBodyParam("password", "xxxxxx")
.post() // POST请求
配置了BaseUrl
之后,如有特殊请求,仍然可以使用全路径的方式,一点都不妨碍:
http.sync("https://www.baidu.com").get()
如何想改变执行回调函数的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建HTTP
时配置如下:
HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
runOnUiThread(run); // 在UI线程执行
})
.build();
该配置影响的回调为:OnResponse
、OnException
和OnComplete
。
与其他封装 OkHttp 的框架不同,HttpUtils 并不会遮蔽 OkHttp 本身就很好用的功能,如下:
HTTP http = HTTP.builder()
.config((Builder builder) -> {
// 配置连接池 最小10个连接(不配置默认为 5)
builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES));
// 配置连接超时时间
builder.connectTimeout(20, TimeUnit.SECONDS);
// 配置拦截器
builder.addInterceptor((Chain chain) -> {
Request request = chain.request();
// 必须同步返回,拦截器内无法执行异步操作
return chain.proceed(request);
});
// 其它配置: SSL、缓存、代理、事件监听...
})
.build();
预处理器(Preprocessor
)可以让我们在请求发出之前根据业务对请求本身做一些处理,但与OkHttp
的拦截器(Interceptor
)不同:预处理器可以让我们异步处理这些问题。
例如,当我们想为请求任务自动添加Token
头信息,而Token
只能通过异步方法requestToken
获取时,这时使用Interceptor
就很难处理了,但可以使用预处理器轻松解决:
HTTP http = HTTP.builder()
.addPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();// 获得当前的HTTP任务
if (!task.isTagged("Auth")) { // 根据标签判断该任务是否需要Token
return;
}
requestToken((String token) -> { // 异步获取 Token
task.addHeader("Token", token);// 为任务添加头信息
chain.proceed(); // 继续当前的任务
});
})
.build();
和Interceptor
一样,Preprocessor
也可以添加多个。
普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当Token
过期时我们需要去刷新获取新Token
,而刷新Token
这个操作只能有一个任务去执行,因为如果n
个任务同时执行的话,那么必有n-1
个任务刚刷新得到的Token
可能就立马失效了,而这是我们所不希望的。
为了解决这个问题,HttpUtils 提供了串行预处理器,它可以让HTTP任务排好队,一个一个地进入预处理器:
HTTP http = HTTP.builder()
.addSerialPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();
if (!task.isTagged("Auth")) {
return;
}
// 检查过期,若需要则刷新Token
requestTokenAndRefreshIfExpired((String token) -> {
task.addHeader("Token", token);
chain.proceed(); // 调用此方法前,不会有其它任务进入该处理器
});
})
.build();
串行预处理器实现了让HTTP任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程!
类HttpUtils
本是 1.x 版本里的最重要的核心类,由于在 2.x 版本里抽象出了HTTP
接口,使得它的重要性已不如往昔。但合理的使用它,仍然可以带来不少便利,特别是在没有IOC容器的环境里,比如在Android开发和一些工具项目的开发中。
类HttpUtils
共定义了四个静态方法:
async(String url)
开始一个异步请求 (内容通过一个HTTP
单例实现)sync(String url)
开始一个同步请求 (内容通过一个HTTP
单例实现)cancel(String tag)
按标签取消请求(内容通过一个HTTP
单例实现)of(HTTP http)
配置HttpUtils
持有的HTTP
实例(不调用此方法前默认使用一个没有没有经过任何配置的HTTP
懒实例)也就是说,能使用http
实例的地方,都可以使用HttpUtils
类,例如:
// 在配置HTTP实例之前,只能使用全路径方式
List<Role> roles = HttpUtils.sync("http://api.demo.com/roles")
.get().getBody().toList(Role.class);
// 配置HTTP实例,全局生效
HttpUtils.of(HTTP.builder()
.baseUrl("http://api.demo.com")
.build());
// 内部使用新的HTTP实例
List<User> users = HttpUtils.sync("/users")
.get().getBody().toList(User.class);
下篇文章:OkHttp 优雅封装 HttpUtils 之 上传下载解密
转载自: https://blog.csdn.net/zh_xu123/article/details/105061724