Http工具包,封装 OkHttp,自动解析,链式用法、异步同步、前后端通用
- 支持异步、同步请求
- 支持Restfull风格
- JSON自动封装与解析
- TCP连接池
- 请求拦截器
- Http2
- 回调线程配置
- 异步预处理器
- GET|POST|PUT|DELETE
- 文件上传下载
当前文档版本 2.0.0 查阅 1.x.x 点我跳转
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>httputils</artifactId>
<version>2.0.0</version>
</dependency>
compile 'com.ejlchina:httputils:2.0.0'
HTTP http = HTTP.builder().build();
HTTP
对象有以下三个方法:
async(String urlPath)
开始一个异步HTTP任务sync(String urlPath)
开始一个同步HTTP任务cancel(String tag)
根据标签批量取消HTTP任务
为了简化文档,下文中出现的http
均是已构建好的HTTP
对象。
使用方法sync(String url)
开始一个同步请求:
// 最终路径 http://api.demo.com/users?name=Jack
User user = http.sync("http://api.demo.com/users")
.addUrlParam("name", "Jack") // 添加查询参数
.get() // 发送GET请求
.getBody() // 获取响应报文体
.toBean(User.class); // 得到目标数据
方法sync
返回一个同步HttpTask
,可链式使用。
使用方法async(String url)
开始一个异步请求:
// 最终路径为 http://api.demo.com/users/1
http.async("http://api.demo.com/users/{id}")
.addPathParam("id", 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 result = http.sync("http://api.demo.com/users").get(); // 同步 GET
HttpCall call = http.async("http://api.demo.com/users")
.setOnResponse((HttpResult result) -> {
}).get(); // 异步 GET
HttpResult result = http.sync("http://api.demo.com/users")
.addBodyParam("name", "Jack")
.addBodyParam("age", 20)
.post(); // 同步 POST
HttpCall call = http.async("http://api.demo.com/users")
.addBodyParam("name", "Jack")
.addBodyParam("age", 20)
.setOnResponse((HttpResult result) -> {
}).post(); // 异步 POST
HttpResult result = http.sync("http://api.demo.com/users/1")
.addJsonParam("name", "Jack")
.put(); // 同步 PUT
HttpCall call = http.async("http://api.demo.com/users/1")
.addJsonParam("name", "Jack")
.setOnResponse((HttpResult result) -> {
})
.put(); // 异步 PUT
HttpResult result = http.sync("http://api.demo.com/users/1").delete(); // 同步 DELETE
HttpCall call = http.async("http://api.demo.com/users/1")
.setOnResponse((HttpResult result) -> {
})
.delete(); // 异步 DELETE
只有异步请求才可以设置回调函数:
http.async("http://api.demo.com/users/1")
.setOnResponse((HttpResult result) -> {
// 响应回调
})
.setOnException((Exception e) -> {
// 异常回调
})
.setOnComplete((State state) -> {
// 完成回调,无论成功失败都会执行
})
.get();
所有的同步请求方法均返回一个 HttpResult 对象,所有的异步请求方法均返回一个 HttpCall 对象。
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
对象,它有如下方法:toBytes()
返回字节数组toByteStream()
返回字节输入流toCharStream()
返回字符输入流toString()
返回字符串toJsonObject()
返回Json对象toJsonArray()
返回Json数组toBean(Class<T> type)
返回根据type自动json解析后的JavaBeantoBean(TypeReference<T> type)
返回根据type自动json解析后的JavaBeantoFile(String filePath)
下载到指定路径并返回保存后的文件(下载文件时非常有用)toFile(File file)
下载到指定文件并返回保存后的文件(下载文件时非常有用)getContentType()
返回报文体的媒体类型getContentLength()
返回报文体的字节长度- 对同一个
Body
对象,以上toXXX()
类方法只能使用一个且仅能使用一次
getError()
执行中发生的异常,自动捕获执行请求是发生的 网络超时、网络错误 和 其它请求异常
例如,下载文件到指定目录:
String path = "D:/reports/2020-03-01.xlsx"; // 文件保存目录
// 同步下载
http.sync("http://api.demo.com/reports/2020-03-01.xlsx")
.get().getBody().toFile(path);
// 异步下载
http.async("http://api.demo.com/reports/2020-03-01.xlsx")
.setOnResponse((HttpResult result) -> {
result.getBody().toFile(path);
})
.get();
HttpCall
对象是异步请求方法( get
、post
、put
、delete
)的返回值,与java
的Future
接口很像,它有如下方法:
cancel()
取消本次请求,返回取消结果isCanceled()
返回请求是否被取消isDone()
返回是否执行完成,包含取消和失败getResult()
返回执行结果HttpResult
对象,若请求未执行完,则挂起当前线程直到执行完成再返回
取消一个异步请求示例:
HttpCall call = http.async("http://api.demo.com/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
与tag
方法用于构建任务本身。
单个添加(同步异步添加方法一样):
http.sync("http://api.demo.com/orders")
.addHeader("Access-Token", "xxxxxx")
.addHeader("Content-Type", "application/json")
.get();
多个添加(同步异步添加方法一样):
Map<String, String> headers = new HashMap<>()
headers.put("Access-Token", "xxxxxx");
headers.put("Accept", "application/json");
http.sync("http://api.demo.com/orders")
.addHeader(headers)
.get();
路径参数用于替换URL字符串中的占位符。
单个添加(同步异步添加方法一样):
http.sync("http://api.demo.com/shops/{shopName}/products/{productId}")
.addPathParam("shopName", "taobao")
.addPathParam("productId", 20)
.get();
多个添加(同步异步添加方法一样):
Map<String, String> params = new HashMap<>()
params.put("shopName", "taobao");
params.put("productId", 20);
http.sync("http://api.demo.com/shops/{shopName}/products/{productId}")
.addPathParam(params)
.get();
查询参数(URL参数)用于拼接在 url 字符串的 ? 之后。
单个添加(同步异步添加方法一样):
http.sync("http://api.demo.com/products")
.addUrlParam("name", "手机")
.addUrlParam("type", "5G")
.get();
多个添加(同步异步添加方法一样):
Map<String, String> params = new HashMap<>()
params.put("name", "手机");
params.put("type", "5G");
http.sync("http://api.demo.com/products")
.addUrlParam(params)
.get();
表单参数(Body参数)以 key=value& 的形式携带与请求报文体内。
单个添加(同步异步添加方法一样):
http.sync("http://api.demo.com/signin")
.addBodyParam("username", "Jackson")
.addBodyParam("password", "xxxxxx")
.post();
多个添加(同步异步添加方法一样):
Map<String, String> params = new HashMap<>()
params.put("username", "Jackson");
params.put("password", "xxxxxx");
http.sync("http://api.demo.com/signin")
.addBodyParam(params)
.post();
JSON 参数最终以 json 字符串的形式携带与请求报文体内。
单个添加(同步异步添加方法一样):
http.sync("http://api.demo.com/signin")
.addJsonParam("username", "Jackson")
.addJsonParam("password", "xxxxxx")
.post();
多个添加(同步异步添加方法一样):
Map<String, String> params = new HashMap<>()
params.put("username", "Jackson");
params.put("password", "xxxxxx");
http.sync("http://api.demo.com/signin")
.addJsonParam(params)
.post();
直接设置JSON字符串:
http.sync("http://api.demo.com/signin")
.setRequestJson("\"username\":\"Jackson\",\"password\":\"xxxxxx\"")
.post();
JavaBean 自动转 JSON:
Login login = new Login();
login.setUsername("Jackson");
login.setPassword("xxxxxx");
http.sync("http://api.demo.com/signin")
.setRequestJson(login)
.post();
上传本地文件:
File file1 = new File("D:/1.jpg");
File file2 = new File("D:/2.jpg");
http.sync("http://api.demo.com/upload")
.addFileParam("image1", file1)
.addFileParam("image2", file2)
.post();
使用文件输入流上传:
// 获得文件的输入流
InputStream input = ...
http.sync("http://api.demo.com/upload")
.addFileParam("image", "jpg", input)
.post();
使用文件字节数组上传:
// 获得文件的字节数组
byte[] content = ...
http.sync("http://api.demo.com/upload")
.addFileParam("image", "jpg", content)
.post();
文件参数和表单参数可以一起添加:
File file = new File("D:/首页广告.jpg");
http.sync("http://api.demo.com/messages")
.addBodyParam("name", "广告图")
.addFileParam("image", file)
.post();
有时候我们对HTTP任务加以分类,这时候可以使用标签功能:
http.async("http://api.demo.com/users")
.tag("MyTag")
.get();
当使用标签后,就可以按标签批量的对HTTP任务进行取消:
http.cancel("MyTag");
也可以在统一配置的预处理器中,以标签对任务进行分类处理,参见并行预处理器与串行预处理器。
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();
与其他封装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((Process process) -> {
HttpTask<?> task = process.getTask(); // 获得当前的请求任务
String tag = task.getTag(); // 取得添加在任务上的标签
if (!"Auth".equals(tag)) { // 根据标签判断该任务是否需要Token
return;
}
requestToken((String token) -> { // 异步获取 Token
task.addHeader("Token", token); // 为任务添加 Token头信息
process.proceed(); // 继续当前的任务
});
})
.build();
和Interceptor
一样,Preprocessor
也可以添加多个。
普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当Token
过期时我们需要去刷新获取新Token
,而刷新Token
这个操作只能有一个任务去执行,因为如果n
个任务同时执行的话,那么必有n-1
个任务刚得刷新得到的Token
可能会立马失效,而这是我们所不希望的。
为了解决这个问题,HttpUtils
提供了串行预处理器,它可以让HTTP任务排好队,一个一个地进入预处理器:
HTTP http = HTTP.builder()
.addSerialPreprocessor((Process process) -> {
HttpTask<?> task = process.getTask(); // 获得当前的请求任务
String tag = task.getTag(); // 取得添加在任务上的标签
if (!"Auth".equals(tag)) { // 根据标签判断该任务是否需要Token
return;
}
// 检查过期,若需要则刷新Token
requestTokenAndRefreshIfExpired((String token) -> {
task.addHeader("Token", token); // 为任务添加 Token头信息
process.proceed(); // 调用此方法前,不会有其它任务进入该处理器
});
})
.build();
串行预处理器实现了让HTTP任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程!
- Fork 本仓库
- 新建 Feat_xxx 分支
- 提交代码
- 新建 Pull Request
- 使用 Readme_XXX.md 来支持不同的语言,例如 Readme_en.md, Readme_zh.md
- 码云官方博客 blog.gitee.com
- 你可以 https://gitee.com/explore 这个地址来了解码云上的优秀开源项目
- GVP 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
- 码云官方提供的使用手册 https://gitee.com/help
- 码云封面人物是一档用来展示码云会员风采的栏目 https://gitee.com/gitee-stars/