OkHttp 源码学习一:基础知识学习

458 阅读12分钟

昨天看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

OkHttpEventListener 是一个用于监控 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

AuthenticatorOkHttp 中处理 HTTP 认证的核心工具。 在 OkHttp 中,Authenticator 是一个用于处理 HTTP 认证(Authentication)的接口,主要用于在服务端要求身份验证时自动提供凭据(如用户名和密码)。

SocketFactory

  • SocketFactoryOkHttp 中是底层通信的核心,用于创建普通的 Socket

  • 自定义 SocketFactory 能实现特殊的网络行为,如调试流量、优化超时。

  • 在大多数情况下,默认的 SocketFactory 已经足够,但需要定制化功能时,自定义是一个灵活的解决方案。

SSLSocketFactory

SocketFactorySSLSocketFactory 的区别

  • 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 在网络请求的底层通信中起着重要作用,它决定了客户端如何与服务器交互。

image.png

HostnameVerifier

在 OkHttp 中,HostnameVerifier 是一个接口,用于在 HTTPS 连接中验证服务器证书中的主机名是否与实际访问的主机名匹配。它是 HTTPS 安全机制的重要组成部分,可以防止中间人攻击(MITM)。juejin.cn/post/745864…

CertificatePinner

OkHttp 中,CertificatePinner 是一个安全功能,用于实现 SSL/TLS 证书固定(Certificate Pinning) 。通过将服务器的公钥或证书与客户端预定义的可信值绑定,可以有效防止中间人攻击(MITM),即使攻击者获得了伪造的可信 CA 证书,也无法欺骗客户端。 juejin.cn/post/745863…

image.png

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的工作流程。

image.png