OkHttp 是一个广泛应用于 Android 和 Java 开发领域的开源网络请求框架,它以其简洁、易用和高性能的特点而闻名。本文就从简单的使用到源码分析来介绍OkHttp。
1.引用资源
dependencies {
implementation(“com.squareup.okhttp3:okhttp:3.14.9”)
implementation("com.squareup.okhttp3:logging-interceptor:3.14.9")
}
具体使用什么版本以自己实际情况为准
官网地址
构建一个OkHttpClient对象(Builder方式)
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client=builder.build()
发送请求
Get方式发送一个同步请求
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client=builder.build()
Request request = new Request.Builder()
.url("你的url")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
// 这里处理你的响应数据,比如解析JSON
String responseBody = response.body().string();
// 现在我们需要在主线程中更新UI或执行其他操作
runOnUiThread(new Runnable() {
@Override
public void run() {
// 在这里更新UI或执行其他主线程任务
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
这里.get()可以省略不写(默认就是get请求),通过调用call.execute()来发送一个同步请求。
Get方式发送一个异步请求
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client=builder.build()
Request request = new Request.Builder()
.url("你的url")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
// 请求失败的处理逻辑
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
// 在这里处理你的响应数据,比如解析JSON
String responseBody = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
// 在这里更新UI或执行其他主线程任务
}
});
}
}
});
Post请求只需将get()换成post即可,
import okhttp3.*;
// ...
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client=builder.build()
// 创建POST请求体,这里使用FormBody作为示例
RequestBody formBody = new FormBody.Builder()
.add("key1", "value1")
.add("key2", "value2")
.build();
Request request = new Request.Builder()
.url("http://example.com/api/post")
.post(formBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
// 请求失败的处理逻辑,可以更新UI显示错误信息
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新UI显示错误信息
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
// 解析响应体
String responseBody = response.body().string();
// 在主线程上更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新UI显示响应内容
}
});
}
}
});
上面就是OkHttp的简单使用了。
源码分析
第一步是构建一个OkHttpClient对象。
通过Builder构建。
可以看到在Builder方法里主要做了一下几个工作
1.初始化dispatcher,并将其赋值给 dispatcher 属性。分发器用于调度和管理网络请求
2.protocols = DEFAULT_PROTOCOLS;:将默认的协议(DEFAULT_PROTOCOLS)赋值给 protocols 属性。默认协议通常包括 HTTP/1.1 和 HTTP/2。
3.connectionSpecs = DEFAULT_CONNECTION_SPECS;:将默认的连接规范(DEFAULT_CONNECTION_SPECS)赋值给 connectionSpecs 属性。连接规范用于配置连接的安全策略和传输协议。
4.eventListenerFactory = EventListener.factory(EventListener.NONE);:创建一个事件监听器工厂,用于生成事件监听器实例。默认情况下,事件监听器为空。
5.proxySelector = ProxySelector.getDefault();:获取系统默认的代理选择器,并将其赋值给 proxySelector 属性。如果系统默认的代理选择器为 null,则创建一个空的代理选择器(NullProxySelector)。
6.cookieJar = CookieJar.NO_COOKIES;:将一个不处理任何 Cookie 的 CookieJar 实例赋值给 cookieJar 属性。这意味着默认情况下不会处理任何 Cookie。
7.socketFactory = SocketFactory.getDefault();:获取系统默认的 Socket 工厂,并将其赋值给 socketFactory 属性。这个 Socket 工厂用于创建 Socket 连接。
8.hostnameVerifier = OkHostnameVerifier.INSTANCE;:将 OkHttp 默认的主机名验证器赋值给 hostnameVerifier 属性。这个验证器用于验证服务器证书的主机名。
9.certificatePinner = CertificatePinner.DEFAULT;:将默认的证书钉住实例赋值给 certificatePinner 属性。这个证书钉住策略用于限制客户端所信任的服务器证书。
10.proxyAuthenticator = Authenticator.NONE;:将一个空的代理认证器实例赋值给 proxyAuthenticator 属性。默认情况下不使用任何代理认证。
11.authenticator = Authenticator.NONE;:将一个空的认证器实例赋值给 authenticator 属性。默认情况下不使用任何认证器。
12.connectionPool = new ConnectionPool();:创建一个新的连接池实例,并将其赋值给 connectionPool 属性。连接池用于管理和复用 TCP 连接。
13.dns = Dns.SYSTEM;:将系统默认的 DNS 解析器赋值给 dns 属性。默认情况下使用系统默认的 DNS 解析器。
14.followSslRedirects = true;:设置是否自动遵循 SSL 重定向。
15.followRedirects = true;:设置是否自动遵循 HTTP 重定向。
16.retryOnConnectionFailure = true;:设置在连接失败时是否自动重试。
17.callTimeout = 0;:设置调用超时时间,默认为 0,即没有超时限制。
18.connectTimeout = 10_000;:设置连接超时时间为 10 秒。
19.readTimeout = 10_000;:设置读取超时时间为 10 秒。
20.writeTimeout = 10_000;:设置写入超时时间为 10 秒。
21.pingInterval = 0;:设置 ping 的间隔时间。
client.newCall
调用了RealCall下面的newRealCall返回了一个RealCall对象。
Transmitter 负责发送请求并接收响应。
同步请求
client.newCall(request).execute()
synchronized代码块中的代码只能被执行一次,这样可以确保在多线程环境下,不会重复执行同一个请求。
transmitter.timeoutEnter();
transmitter.callStart();
这两行代码执行了网络请求前的一些准备工作,包括开始计时超时时间和标记请求开始执行
client.dispatcher().executed(this);调用了Dispatcher的executed,把RealCall对象添加到正在执行的同步请求列表中。
client.dispatcher().finished(this);调用了Dispatcherl里的finished。
promoteAndExecute() 方法遍历了 readyAsyncCalls 队列去寻找能够执行的 AsynCall,若找到,会判断runningAsyncCalls的长度是否大于
最大请求书maxRequests,如果大于等于break,否则检查当前异步调用所在主机的并发请求数是否已达到最大限制 maxRequestsPerHost,如果是,则跳过该异步调用,继续处理下一个。这个检查确保不会超出每个主机的最大请求数。
如果都不满足则从readyAsyncCalls列表中移除异步调用并加到runningAsyncCalls中。
最终遍历临时的可执行队列,依次执行。
executorService()线程池没有核心线程,线程个数最大为Integer.MAX_VALUE,空闲时线程存活时间为60s。
最后执行client.dispatcher().finished(),再说同步请求时我们已经介绍了这个方法,上面红色框圈起来的代码又执行了promoteAndExecute,
就是说每当一个任务执行完毕后,会重新遍历readyAsyncCalls,然后 把任务依次加到runningAsyncCalls。
getResponseWithInterceptorChain
getResponseWithInterceptorChain是真正获取response的地方,是整个OkHttp的核心,采用了责任链模式。
通过拦截器构建了以RealInterceptorChain责任链,通过process 方法开始依次执行链上的每个节点,并返回处理后的 Response。
可以看到这个方法有五个拦截器
1.RetryAndFollowUpInterceptor(重试和重定向拦截器)
第一个接触到请求,最后接触到响应的拦截器,负责判断是否需要重新发起整个请求,负责处理和错误重试和重定向的,确保了请求的可靠性可正确定。
2.BridgeInterceptor(桥接拦截器)
补全请求,并对响应进行额外处理,主要用于在 HTTP 请求和 WebSocket 请求之间进行桥接转换。
3.CacheInterceptor(缓存拦截器)
处理缓存相关的逻辑,提高了网络请求的效率和性能,并提供了对缓存策略的灵活控制。
4.ConnectInterceptor(链接拦截器)
与服务器完成TCP连接 (Socket),内部维护一个连接池,负责连接复用、创建连接、释放连接。
5.CallServerInterceptor(请求服务拦截器)
与服务器通信,负责处理网络请求的发送和接收工作,封装请求数据与解析响应数据。
异步请求
client.newCall(request).enqueue()
调用RealCall的enqueue(记住这里传的是AsyncCall)
最终调用的是Dispatcher的enqueue
promoteAndExecute方法里最后执行executeOn
里面执行了executorService.execute(this);
execute传入Runbale,会执行他的run方法。
AsyncCall继承了NameRunuable
NameRunuable实现了Runable,run方法执行了execute(),又回到了RealCall里的execute(),具体分析上面已经介绍过了。
优点
1.简洁易用的 API:
OkHttp 提供了简洁易用的 API,使得进行网络请求变得非常方便。通过简洁的链式调用,可以轻松地构建请求、设置请求头、添加参数等操作,同时支持同步和异步请求。
2.支持 HTTP/2 和 SPDY协议:
OkHttp 支持 HTTP/2 和 SPDY,这两种协议都是现代网络通信的重要协议,能够提升网络请求的性能和效率。
3.连接池和请求复用:
OkHttp 内置了连接池和请求复用机制,可以有效地减少网络请求的延迟和资源消耗。通过连接池,可以复用已经建立的连接,避免频繁地创建和销毁连接,提高了请求的效率。
4.自动压缩和解压缩:
OkHttp 支持自动压缩和解压缩数据,可以减少网络传输的数据量,提高传输效率。
5.请求重试和超时设置:
OkHttp 提供了灵活的请求重试和超时设置机制,可以根据实际需求进行配置,保证网络请求的稳定性和可靠性。
6.拦截器机制:
OkHttp 使用拦截器机制来实现功能的扩展和定制。通过拦截器,可以灵活地添加、修改或者删除请求或响应的头信息,实现诸如日志记录、重试、缓存等功能。
7.支持 HTTPS:
OkHttp 支持 HTTPS 协议,能够通过 TLS/SSL 来确保通信的安全性,同时支持证书验证和域名校验,保护网络通信的安全。
8.高性能:
OkHttp 在性能方面表现优异,具有较低的延迟和资源消耗,能够处理大量并发请求,适用于高性能的网络通信场景。
总结
OkHttp