昨天看Google DevFest的录制视频,其中有一个分享者我觉得说的挺好,源码是性价比最高的,比起最新的技术栈,在搞懂源码之后,很长的一段时间之内都可以一直使用在源码中学得的知识。心血来潮打算找个源码看看。看了网上很多文章,大多数都有看源码学习经验的作者所写,对于我来说不太友好,自己想了一下使用场景,就从最简单的例子入手,我先这样,再那样,最后在这样,哈哈哈哈哈哈,话不多说,开始吧。
Okhttp使用
首先看一下怎么使用okhttp吧。写一个最最最简单的例子,别问为什么,因为我也是第一次用okhttp,哈哈哈哈,中厂开发2年,第一次用Okhttp。
public class OkHttpExample {
public static void main(String[] args) {
String url = "https://raw.github.com/square/okhttp/master/README.md";
try {
String response = run(url);
System.out.println(response); // 打印响应内容
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的代码就是一个调用的地方,为了方便分析我们把Okhttp相关的代码都放入run()方法中,具体请看下面的方法中的代码。
public static String run(String url) throws IOException {
OkHttpClient client = new OkHttpClient(); // 创建 OkHttpClient 实例
Request request = new Request.Builder() //构建Request对象,传入Url
.url(url)
.build();
//使用OkHttpClient的newCall方法创建一个Call对象,发送请求体
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) { //检查Response对象的状态码,如果不是成功的,抛出一个IOException。
throw new IOException("Unexpected code " + response);
}
return response.body().string();
}
}
针对上面的代码,创建了几个与http请求相关的对象,首先应该搞懂这些对象的含义以及功能。
-
OkHttpClient-
官方解释:Factory for calls, which can be used to send HTTP requests and read their responses.
-
OkHttp 库中的核心类,用于管理和执行 HTTP 请求、读取服务端响应。
-
-
Request- 用于定义 HTTP 请求(如 URL、请求头、请求体等)。
-
Call- 表示一个准备好的 HTTP 请求,可以同步或异步执行。
-
Response- 表示服务器对 HTTP 请求的响应。
好了,经过上面的分析,大体知道Okhttp是先创建一个OkHttpClient对象,然后通过调用OkHttpClient.call()方法,将构造好的请求体Request作为参数,生成一个call对象,通过execute()执行请求,最后可以通过对服务端的响应Response做解析处理,获取我们想要使用的数据。
下面我们将整个流程拆解下,看一下每一个对象的创建过程中做了哪些事情,底层逻辑是什么。
OkHttpClinent创建
OkHttpClient client = new OkHttpClient(); // 创建 OkHttpClient 实例
进入OkHttpClient对象可以看到,有一个默认构造函数。它重载了另一个Builder对象(如果对重载是什么不了解的话,需要去google下,不要糊弄过去了),Builder里是什么呢?做了那个操作呢?
constructor() : this(Builder())
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher()
internal var connectionPool: ConnectionPool = ConnectionPool()
internal val interceptors: MutableList<Interceptor> = mutableListOf()
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
internal var followRedirects = true
internal var followSslRedirects = true
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
internal var cache: Cache? = null
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
internal var callTimeout = 0
internal var connectTimeout = 10_000
internal var readTimeout = 10_000
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
internal var routeDatabase: RouteDatabase? = null
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
this.networkInterceptors += okHttpClient.networkInterceptors
this.eventListenerFactory = okHttpClient.eventListenerFactory
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
this.authenticator = okHttpClient.authenticator
this.followRedirects = okHttpClient.followRedirects
this.followSslRedirects = okHttpClient.followSslRedirects
this.cookieJar = okHttpClient.cookieJar
this.cache = okHttpClient.cache
this.dns = okHttpClient.dns
this.proxy = okHttpClient.proxy
this.proxySelector = okHttpClient.proxySelector
this.proxyAuthenticator = okHttpClient.proxyAuthenticator
this.socketFactory = okHttpClient.socketFactory
this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
this.connectionSpecs = okHttpClient.connectionSpecs
this.protocols = okHttpClient.protocols
this.hostnameVerifier = okHttpClient.hostnameVerifier
this.certificatePinner = okHttpClient.certificatePinner
this.certificateChainCleaner = okHttpClient.certificateChainCleaner
this.callTimeout = okHttpClient.callTimeoutMillis
this.connectTimeout = okHttpClient.connectTimeoutMillis
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
this.routeDatabase = okHttpClient.routeDatabase
}
Builder 类有两个构造函数。第一个构造函数是一个无参构造函数,它初始化了 dispatcher 和 connectionPool...... 属性,并创建了空的 interceptors 列表。第二个构造函数接受一个 OkHttpClient 对象作为参数,它使用传入的 OkHttpClient 对象的属性来初始化自己的 dispatcher 和 connectionPool ......属性。
OK,看这个构造函数,就知道大的要来了,这里面初始化了很多对象与变量,他们都是啥呢,我觉得得先看下,了解个大概,后面在再逐一了解下。
dispatcher: Dispatcher //网络请求分发器
connectionPool: ConnectionPool //网络连接管理(连接池)
interceptors: MutableList<Interceptor> //拦截器管理器(这个是什么?不太懂)
networkInterceptors: MutableList<Interceptor> //网络//拦截器管理器?
eventListenerFactory: EventListener.Factory //事件监听器,OkHttpClient. newCall初始化
retryOnConnectionFailure = true //是否重试(无法访问的 IP 地址,过时的池连接,无法访问代理服务器),上面的情况会自动重试,需要设置为false
authenticator: Authenticator //身份验证器
followRedirects = true //重定向配置,默认遵循重定向
followSslRedirects = true //ssl重定向(followRedirects优先级高)
cookieJar: CookieJar //为 HTTP cookie 提供策略和持久性(会存cookie)
cache: Cache? = null //缓存对文件系统的 HTTP 和 HTTPS 响应()
dns: Dns = Dns.SYSTEM //设置DNS服务类型
proxy: Proxy? = null //代理设置
proxySelector: ProxySelector? = null //代理设置具体实现类
proxyAuthenticator: Authenticator //代理服务身份验证器
socketFactory: SocketFactory = SocketFactory.getDefault() //socket工厂类
sslSocketFactoryOrNull: SSLSocketFactory? = null //创建ssl类型的socket
x509TrustManagerOrNull: X509TrustManager? = null //X509证书认证
connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS //套接字连接的配置
protocols: List<Protocol> = DEFAULT_PROTOCOLS //配置该客户端用于与远程服务器通信的协议
hostnameVerifier: HostnameVerifier = OkHostnameVerifier //设置用于确认响应证书(Https)
certificatePinner: CertificatePinner = CertificatePinner.DEFAULT //配置可信证书
certificateChainCleaner: CertificateChainCleaner? = null //计算有效证书链
callTimeout = 0 //请求超时时间(解析DNS、连接、写入请求体、服务器处理、读取响应体)
connectTimeout = 10_000 //链接超时时间
readTimeout = 10_000 //读取响应超时时间
writeTimeout = 10_000 //写入耗时超时时间
pingInterval = 0 //设置此客户端发起的 HTTP 2 和 Web 套接字 ping 之间的时间间隔
minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE //设置将被压缩的最小出站 Web 套接字消息大小
internal var routeDatabase: RouteDatabase? //创建到目标地址的新连接时要避免的失败路由黑名单
初始化参数详解
Dispatcher
Dispatcher 是一个用于调度网络请求的组件,它负责将请求分发到不同的线程或线程池中执行。 具体可以看下面这个介绍: juejin.cn/post/745087…
connectionPool
ConnectionPool 即连接池,用来管理 HTTP 和 HTTP/2 连接的重用,以减少网络延迟 具体可以看下面这个介绍: juejin.cn/post/745085…
Interceptor
在 OkHttp 中,Interceptor 是一个非常强大的功能,用于拦截、修改、观察或终止 HTTP 请求和响应的处理流程。它主要在 网络请求的生命周期 中插入自定义逻辑,从而实现灵活的操作。
具体详细源码可见: juejin.cn/post/745104…
EventListener
OkHttp 的 EventListener 是一个用于监控 HTTP 请求与响应事件的机制。通过 EventListener,你可以深入了解 HTTP 请求的执行过程,包括连接建立、数据传输、响应接收等细节,适合进行性能监控、调试和故障排查。
相关文章请看链接: juejin.cn/post/745107…
retryOnConnectionFailure
retryOnConnectionFailure 是一个非常有用的配置选项,它决定了在发生连接失败(如网络超时、服务器不可达等)时,OkHttp 是否应该自动重试请求。
相关文章请看链接: juejin.cn/post/745119…
Authenticator
看名字像是权限认证相关,源码注解为:在连接到代理服务器之前执行抢先式身份验证,或者在收到来自源 Web 服务器或代理服务器的质询后执行反应式身份验证。 相关文章请看链接: juejin.cn/post/745142…
followRedirects
是否支持重定向,默认是支持重定向。followRedirects 是一个配置选项,它用于控制是否自动跟随重定向响应。这个选项通常在 OkHttpClient 的配置中设置,用于决定当服务器返回3xx状态码(表示重定向的HTTP状态码)时,客户端是否应该自动处理重定向,而不是将重定向响应返回给应用层。 当 followRedirects 设置为 true 时,客户端会自动处理重定向,它会根据 Location 头部信息自动重定向到新的URL。这对于大多数应用来说是一个合理的行为,因为它简化了处理重定向的逻辑。
CookieJar
CookieJar 是一个接口,用于管理 HTTP 请求和响应中的 Cookie。它允许开发者灵活地控制 Cookie 的存储和使用方式。
juejin.cn/post/745144…
Cache
缓存对文件系统的 HTTP 和 HTTPS 响应,以便重复使用它们,从而节省时间和带宽。 Cache 实例必须具有对该目录的独占访问权限,否则内部数据结构可能会导致损坏或运行时错误。然而,它可以在多个 OkHttpClient 实例之间共享。 juejin.cn/spost/74519…
Dns
在 OkHttp 中,DNS(域名系统)用于将主机名解析为 IP 地址,以便发起网络请求时能找到目标服务器。juejin.cn/post/745821…
Proxy
设置此客户端创建的连接将使用的 HTTP 代理。这优先于 proxySelector,仅当此代理为空(默认情况下为空)时才会采用 proxySelector。要完全禁用代理使用,请调用 proxy(Proxy.NO_PROXY)。
proxySelector
如果未显式指定 [proxy][proxy],则设置要使用的代理选择策略。代理选择器可能返回多个代理;在这种情况下,将按顺序尝试它们,直到建立成功的连接。如果未设置,将使用[系统范围默认][ProxySelector.getDefault]代理选择器。
Authenticator
Authenticator 是 OkHttp 中处理 HTTP 认证的核心工具。
在 OkHttp 中,Authenticator 是一个用于处理 HTTP 认证(Authentication)的接口,主要用于在服务端要求身份验证时自动提供凭据(如用户名和密码)。
SocketFactory
-
SocketFactory在OkHttp中是底层通信的核心,用于创建普通的Socket。 -
自定义
SocketFactory能实现特殊的网络行为,如调试流量、优化超时。 -
在大多数情况下,默认的
SocketFactory已经足够,但需要定制化功能时,自定义是一个灵活的解决方案。
SSLSocketFactory
SocketFactory 与 SSLSocketFactory 的区别:
SocketFactory负责普通的 TCP 套接字。SSLSocketFactory继承自SocketFactory,用于创建安全的SSL/TLS套接字。
ConnectionSpec
OkHttp 中的 ConnectionSpec 是一个类,用于定义客户端和服务器之间的连接配置,主要包括协议版本(如 TLS 或 HTTP/1.1)、加密套件(Cipher Suites)和其他安全设置。
juejin.cn/post/745822…
Protocol
在 OkHttp 中,Protocol 是一个枚举类型,用于表示客户端与服务器之间支持的通信协议,例如 HTTP/1.1、HTTP/2 和 QUIC。Protocol 在网络请求的底层通信中起着重要作用,它决定了客户端如何与服务器交互。
HostnameVerifier
在 OkHttp 中,HostnameVerifier 是一个接口,用于在 HTTPS 连接中验证服务器证书中的主机名是否与实际访问的主机名匹配。它是 HTTPS 安全机制的重要组成部分,可以防止中间人攻击(MITM)。juejin.cn/post/745864…
CertificatePinner
在 OkHttp 中,CertificatePinner 是一个安全功能,用于实现 SSL/TLS 证书固定(Certificate Pinning) 。通过将服务器的公钥或证书与客户端预定义的可信值绑定,可以有效防止中间人攻击(MITM),即使攻击者获得了伪造的可信 CA 证书,也无法欺骗客户端。
juejin.cn/post/745863…
CertificateChainCleaner
在 OkHttp 中,CertificateChainCleaner 是一个工具类,用于清理和验证服务器证书链(Certificate Chain)。它主要用于确保客户端接收到的证书链是可信的,并且能够正确地通过本地的信任管理器(TrustManager)的验证。
juejin.cn/post/745863…
callTimeout
默认调用超时(以毫秒为单位)。默认情况下,完整调用没有超时,但调用中的连接、写入和读取操作有超时。对于 WebSocket 和双工调用,超时仅适用于初始设置
connectTimeout
默认连接超时(以毫秒为单位)。默认值为 10 秒。
readTimeout
默认读取超时(以毫秒为单位)。默认值为 10 秒。
writeTimeout
默认写入超时(以毫秒为单位)。默认值为 10 秒。
pingInterval
Web 套接字和 HTTP 2 ping 间隔(以毫秒为单位)。默认情况下不发送 ping(所以一般用不到)。
webSocketCloseTimeout
Web 套接字关闭超时(以毫秒为单位)。
minWebSocketMessageToCompress
将被压缩的最小出站 Web 套接字消息大小(以字节为单位)。默认值为 1024 字节。
RouteDatabase
在 OkHttp 中,RouteDatabase 是一个内部组件,用于管理和记录连接路由(Route)的历史信息。它在连接复用和故障恢复中扮演着重要角色,帮助 OkHttp 在遇到网络问题时快速切换到可用的路由。
juejin.cn/post/745863…
创建到目标地址的新连接时要避免的失败路由的拒绝列表。这样做是为了让 OkHttp 能够从错误中吸取教训:如果尝试连接到特定 IP 地址或代理服务器时出现故障,则会记住该故障并首选备用路由。
TaskRunner
在 OkHttp 中,TaskRunner 是一个核心组件,用于管理和调度异步任务。它提供了线程池和任务队列的能力,确保 OkHttp 的内部任务(如连接维护、请求队列处理、HTTP/2 流管理等)能够高效、可靠地执行。
juejin.cn/post/745865…
在一组任务队列之间共享的一组工作线程。对于使用守护线程的任务运行程序,请使用 INSTANCE。当前没有非守护线程的共享实例。任务运行程序还负责在卸载库时释放保留的线程。这是为了实现代码卸载的容器环境的好处。大多数应用程序应该共享进程范围的 TaskRunner 并使用队列来执行每个客户端的工作。
好了搞了两个周,总算是知道,okhttp的一些基本功能和成员变量的作用了,具体的细节当然是很麻烦琐碎的了,好的,下面可以通过一个OKHTTP的请求,去了解okhttp的工作流程。