曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果。如果觉得本文有用,记得关注我,我将带给你更多。

介绍

OkHttps 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听过程控制功能,在轻松解决很多原本令人头疼问题的同时,设计上也力求纯粹与优雅。

  • 链式调用,一点到底
  • BaseURL、URL占位符、JSON自动封装与解析
  • 同步拦截器、异步预处理器、回调执行器
  • 文件上传下载(过程控制、进度监听)
  • TCP连接池、Http2

中文官网:http://okhttps.ejlchina.com/

项目地址

Gitee:https://gitee.com/ejlchina-zhxu/okhttps

GitHub:https://github.com/ejlchina/okhttps

安装教程

Maven

<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>1.0.5</version>
</dependency>

Gradle

compile 'com.ejlchina:okhttps:1.0.5'

使用说明

1 简单示例

1.1 构建 HTTP

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均是在构建时设置了BaseUrlHTTP实例。

1.2 同步请求

  使用方法sync(String url)开始一个同步请求:

List<User> users = http.sync("/users") // http://api.demo.com/users
.get() // GET请求
.getBody() // 获取响应报文体
.toList(User.class); // 得到目标数据

  方法sync返回一个同步HttpTask,可链式使用。

1.3 异步请求

  使用方法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,可链式使用。

2 请求方法(GET|POST|PUT|DELETE)

  同步与异步的HttpTask都拥有getpostputdelete方法。不同的是:同步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

3 解析请求结果

3.1 回调函数

  只有异步请求才可以设置回调函数:

http.async("/users/{id}")             // http://api.demo.com/users/1
.addPathParam("id", 1)
.setOnResponse((HttpResult result) -> {
// 响应回调
})
.setOnException((Exception e) -> {
// 异常回调
})
.setOnComplete((State state) -> {
// 完成回调,无论成功失败都会执行
})
.get();

3.2 HttpResult

  HttpResult是HTTP请求执行完后的结果,它是同步请求方法( getpostputdelete)的返回值,也是异步请求响应回调(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解析后的JavaBean
    • toList(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 之 上传下载解密

3.3 HttpCall

  HttpCall对象是异步请求方法(getpostputdelete)的返回值,与javaFuture接口很像,它有如下方法:

  • 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

4 构建HTTP任务

  HTTP对象的syncasync方法返回一个HttpTask对象,该对象提供了可链式调用的addXXXsetXXX系列方法用于构建任务本身。

  • 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头信息,用于分块下载

5 使用标签

  有时候我们想对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任务才可以被取消。标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见下文[6.4 并行预处理器]与[6.5 串行预处理器]。

6 配置 HTTP

6.1 设置 BaseUrl

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()

6.2 回调执行器

  如何想改变执行回调函数的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建HTTP时配置如下:

HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
runOnUiThread(run); // 在UI线程执行
})
.build();

  该配置影响的回调为:OnResponseOnExceptionOnComplete

6.3 配置 OkHttpClient

  与其他封装 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();

6.4 并行预处理器

  预处理器(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也可以添加多个。

6.5 串行预处理器

  普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当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任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程!

7 使用 HttpUtils 类

  类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 之 上传下载解密


曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果。如果觉得本文有用,记得关注我,我将带给你更多。

OkHttp 优雅封装 HttpUtils 之 气海雪山初探的更多相关文章

  1. OkHttp 优雅封装 HttpUtils 之 上传下载解密

    曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果.如果觉得本文有用,记得关注我,我将带给你更多. 还没看过第一篇文章的欢迎移步:OkHttp 优雅封装 HttpUtils 之气海雪山初 ...

  2. OkHttp 优雅封装 OkHttps 之 回调线程魔变

    第一篇:OkHttp 优雅封装 HttpUtils 之 气海雪山初探 第二篇:OkHttp 优雅封装 HttpUtils 之 上传下载解密 简介 HttpUtils 从 v2.3.0 之后便重命名了, ...

  3. Android开发技巧——BaseAdapter的另一种优雅封装

    RecyclerView虽然因其灵活性.高效性等特点而备受好评,但也不是一定得用它把ListView给替代掉.在某些场景中,ListView还是相对更适合的.比如数据量不大,不频繁更新,并且需要简单地 ...

  4. android 开发 - 使用okhttp框架封装的开发框架

    概述 在android开发中经常要访问网络,目前最流行的网络访问框架就是Okhttp了,然而我们在具体使用时,往往仍然需要二次封装.我使用Builder设计模式进行了封装形成oknet开源库. 介绍 ...

  5. 基于OkHttp的封装库TigerOkHttp的使用

    在前面熟悉了OkHttp的用法之后,为了简化用法同时适用于我的项目,我针对OkHttp进行了更进一步的封装(源码及其Demo地址在https://github.com/huyongli/TigerOk ...

  6. 安卓OKhttp请求封装

    目前安卓开发中使用的网络工具为OKhttp,但是okhttp的使用还不是很方便,在okhttp的基础上再对请求进行封装会极大的方便网络调用. 下面直接上代码. 请求封装 public class Ht ...

  7. Okhttp实用封装

    概述 对okhttp的get,put,delete,post请求简单封装,减少了不必要的冗余代码 详细 代码下载:http://www.demodashi.com/demo/11101.html 在自 ...

  8. OKhttp的封装(下)

    OKhttpManager2.Class  请求工具类 package com.example.administrator.okhttp3; import android.os.Handler; im ...

  9. OKhttp的封装(上)

    自从介绍了OKhttp3的一些基本使用之后,又偷了下懒,所以它的续篇被搁置了一段时间,现在补充. OKhttpManager.Class  请求工具类 package com.example.admi ...

随机推荐

  1. 为什么你的程序配了classpath还是找不到类

    classpath简介 classpath是java程序时拥有的一个系统变量,这个变量可以通过如下方式获取 System.out.println(System.getProperty("ja ...

  2. java后台调用文件上传接口

    借鉴:https://blog.csdn.net/yjclsx/article/details/70675057 /** * 调用流程上传文件接口上传文件 * @param url * @param ...

  3. MySql InnoDB中的锁研究

    # MySql InnoDB中的锁研究 ## 1.InnoDB中有哪些锁### 1. 共享和排他(独占)锁(Shared and Exclusive Locks) InnoDB实现标准的行级锁定,其中 ...

  4. 一份从入门到精通NLP的完整指南 | NLPer

    该小博主介绍 本人:笔名zenRRan,方向自然语言处理,方法主要是深度学习. 未来的目标:人工智能之自然语言处理博士. 写公众号目的:将知识变成开源,让每个渴求知识而难以入门人工智能的小白以及想进阶 ...

  5. .Net Core2.2 使用 AutoMapper进行实体转换

    一.遇到的问题 在. Core Api 的编写中,我们经常会对一些功能点进行新增编辑操作,同时我们有时也会进行查询,但是我们查询的表的数据与我们返回的数据相差甚大,这是我们有需要自己手动进行类型的转换 ...

  6. React Hooks Typescript 开发的一款 H5 移动端 组件库

    CP design 使用 React hooks Typescript 开发的一个 H5 移动端 组件库 English | 简体中文 badge button icon CP Design Mobi ...

  7. BurpSuit工具安装和基本使用方法

    burpsuite是渗透的必备工具,使用它可以进行一些截包分析,修改包数据.暴力破解.扫描等功能,使用最多的场景应该是设置代理拦截数据包分析数据和爆破. JDK工具下载和安装(可选) 运行BurpSu ...

  8. Kaggle竞赛入门(二):如何验证机器学习模型

    本文翻译自kaggle learn,也就是kaggle官方最快入门kaggle竞赛的教程,强调python编程实践和数学思想(而没有涉及数学细节),笔者在不影响算法和程序理解的基础上删除了一些不必要的 ...

  9. 细数Java项目中用过的配置文件(ini 篇)

    Java 菜鸟,会把可变的配置信息写死在代码里:Java 老鸟,会把可变的配置信息提取到配置文件中.坊间流传这么一句非科学的衡量标准,来评判程序员的级别. 那么,项目中的配置信息,你平时都是怎样来实现 ...

  10. 使用Pytorch在多GPU下保存和加载训练模型参数遇到的问题

    最近使用Pytorch在学习一个深度学习项目,在模型保存和加载过程中遇到了问题,最终通过在网卡查找资料得已解决,故以此记之,以备忘却. 首先,是在使用多GPU进行模型训练的过程中,在保存模型参数时,应 ...