Java中发送Http请求之OkHttpClient 1 O

这是我参与11月更文挑战的第13天,活动详情查看:11月更文挑战

Java中Http请求的方式很多, OkHttpClient因其独特的特性,非常适合在常见的场景中使用.

1 OkHttpClient的简介

1 OkHttpClient说明

OkHttpClient是一个高效的HTTP客户端,其特性包含:

  • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
  • 连接池减少请求延时
  • 透明的GZIP压缩减少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求

2 OkHttpClient使用步骤

  • 创建OkHttpClient对象
  • 创建Request对象
  • 将Request 对象封装为Call
  • 通过Call 来执行同步或异步请求,调用execute方法同步执行,调用enqueue方法异步执行

3 OkHttpClient案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码@Slf4j
public class OkHttpDemo {

// 创建OkHttpClient对象, 并设置超时时间 添加拦截器LoginInterceptor
private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoginInterceptor())
.build();

public static void main(String[] args) throws IOException {
String url = "http://www.baidu.com";
Request request = new Builder()
.url(url)
.get() // 不写,默认是GET请求
.build();

Call call = okHttpClient.newCall(request);
// 异步调用
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
log.info("请求失败,异常信息为: {}", e.getMessage());
}

@Override
public void onResponse(@NotNull Call call, @NotNull Response response)
throws IOException {
log.info("请求成功,返回信息为: {}", response.body().toString());
}
});
}
}

Interceptor拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码@Slf4j
public class LoginInterceptor implements Interceptor {

/**
* 统计登录接口完成时间
*/
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {

Request request = chain.request();
long begin = System.currentTimeMillis();
Response response = chain.proceed(request);
long end = System.currentTimeMillis();

// 请求路径中有/login 统计花费时间
if (request.url().toString().contains("/login")) {
log.info("接口处理总用时: {} ", end - begin);
}

return response;
}
}

OkHttpClient对象

通过其内部类Builder的构造器模式,进行属性参数的初始化,常见的包括: 任务调度,协议,连接池,连接超时,读取超时等属性.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码 public static final class Builder {
@NotNull
private Dispatcher dispatcher;
@NotNull
private ConnectionPool connectionPool;
@NotNull
private final List interceptors;
private int callTimeout;
private int connectTimeout;
private int readTimeout;
private int writeTimeout;
private int pingInterval;
private long minWebSocketMessageToCompress;
@Nullable
private RouteDatabase routeDatabase;

}

Request对象

通过其内部类构造器模式,进行属性参数的初始化,常见的包括: 请求地址url,请求方式,请求头,请求体,标签参数等, 并且该构造中默认是GET请求.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码  open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null

/** A mutable map of tags, or an immutable empty map if we don't have any. */
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()

constructor() {
this.method = "GET"
this.headers = Headers.Builder()
}

internal constructor(request: Request) {
this.url = request.url
this.method = request.method
this.body = request.body
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
this.headers = request.headers.newBuilder()
}
}

Call对象

通过OkHttpClient和Request对象构造Call对象,Call接口的唯一实现类RealCall. 其execute方法表示同步执行, enqueue方法表示异步执行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码    override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }

timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}

override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }

callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}

Interceptor拦截器

Interceptor为所有拦截器的接口, 其实现类有 桥接拦截器BridgeInterceptor, 缓存拦截器CacheInterceptor, 服务拦截器CallServerInterceptor, 错误、重定向拦截器RetryAndFollowUpInterceptor, 连接拦截器ConnectInterceptor.

Call中execute方法调用的getResponseWithInterceptorChain()方法, 创建一个拦截器集合容器,首先添加用户自定义的拦截器, 错误、重定向拦截器,桥接拦截器,缓存拦截器,连接拦截器,服务拦截器.

整个拦截器执行链路,按照添加先后顺序执行,即先执行用户自定义拦截器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
java复制代码  internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)

val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)

var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}

4 OkHttpClient常用工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
java复制代码@Slf4j
public class OkHttpUtils {

// 创建OkHttpClient对象, 并设置超时时间
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build();

/**
* 同步GET请求
*
* @param url 请求地址
*/
public static String getRequest(String url) {
try {
// 1 创建OkHttpClient对象
// 2 构建Request对象
Request request = new Request.Builder()
.get()// 不写默认为GET请求
.url(url)
.build();
// 3 发起请求获取响应值
Response response = client.newCall(request).execute();
// 4 根据响应结果判断
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new RuntimeException("请求异常,错误码为: " + response.code());
}
} catch (Exception e) {
log.info("请求失败,错误信息为= {} ", e.getMessage());
}
return null;
}

/**
* 同步POST请求
*
* @param url 请求地址
* @param params 请求参数
*/
public static String postRequest(String url, Map<String, String> params) {

try {
// 1 创建OkHttpClient对象
// 2 构建请求体
MultipartBody body = new MultipartBody.Builder()
.setType(MediaType.parse("multipart/form-data"))
.addFormDataPart("username", params.get("username"))
.addFormDataPart("password", params.get("password"))
.build();
// 3 构建Request对象
Request request = new Request.Builder()
.post(body)
.url(url)
.build();
// 4 发起请求获取响应值
Response response = client.newCall(request).execute();

// 5 根据响应结果判断
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new RuntimeException("请求异常,错误码为: " + response.code());
}
} catch (Exception e) {
log.info("请求失败,错误信息为= {} ", e.getMessage());
}
return null;
}


/**
* 同步GET请求
*/
public static String getRequest(String url) throws IOException {
Request request = new Builder().url(url).build();
Response response = execute(request);

if (response.isSuccessful()) {
return response.body().string();
} else {
throw new ArithmeticException("请求异常,错误码为: " + response.code());
}

}

/**
* 同步请求
*/
public static Response execute(Request request) throws IOException {
return client.newCall(request).execute();
}

/**
* 开启异步线程访问网络, 需要返回结果
*/
public static void enqueue(Request request, Callback callback) {
client.newCall(request).enqueue(callback);
}

/**
* 开启异步线程访问网络,不需要返回结果( Callback 返回为空)
*/
public static void enqueue(Request request) {
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
log.info("请求失败,异常信息为: {} ", e.getMessage());
}

@Override
public void onResponse(@NotNull Call call, @NotNull Response response)
throws IOException {
log.info("请求成功");
}
});
}

}

参考资料:

blog.csdn.net/workingman_…

blog.csdn.net/weixin_4477…

blog.csdn.net/sinat_34241…

square.github.io/okhttp/3.x/…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%