美文网首页再读Android源码系列
2025 再读Android Okhttp源码

2025 再读Android Okhttp源码

作者: 小仙女喂得猪呀 | 来源:发表于2025-06-26 17:17 被阅读0次

Okhttp源码分析

一. 请求流程分析跟踪

 private fun createOkHttpClient(): OkHttpClient {
        // 设置缓存(10MB)
        val cacheSize = 10 * 1024 * 1024 // 10 MB

        //        Cache cache = new Cache(new File(getCacheDir(), "okhttp_cache"), cacheSize);
        return OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS) // 连接超时
            .readTimeout(15, TimeUnit.SECONDS) // 读取超时
            .writeTimeout(15, TimeUnit.SECONDS) // 写入超时
            //                .cache(cache)                         // 缓存
            .build()
    }

    //调用
    val request: Request = Request.Builder()
            .url(url)
            .build()
     okHttpClient.newCall(request).execute()

1. OkHttpClient类

OkHttpClient类的主要职责就是让用户配置一些请求参数(如interceptor、connectTimeout等等),采用建造者模式通过Builder类去配置OkHttpClient的成员变量,最后调用build方法创建出OkHttpClient实例。
在OkHttpClient类中的主要成员变量有:

  • dispatcher: Dispatcher,用于调度网络请求的分发器
  • interceptors: MutableList <Interceptor>,拦截器集合
  • networkInterceptors: MutableList <Interceptor>,用户自定义的网络拦截器
  • connectionPool: ConnectionPool,连接池
  • protocols: List <Protocol>,支持的Http协议版本,即HTTP/1.1、HTTP/2等
  • cache: Cache,缓存配置,默认是没有
  • cookieJar: CookieJar,cookie配置
  • callTimeout: Int,请求超时,默认是0
  • connectTimeout: Int,连接超时,默认10秒
  • readTimeout: Int,读取超时,默认10秒
  • writeTimeout: Int,写入超时,默认10秒
  • pingInterval: Int,发送ping指令间隔,与WebSocket有关,为了保持长连接
  • retryOnConnectionFailure: Boolean,连接失败是否重连
  • followRedirects: Boolean,是否重定向
  • ollowSslRedirects: Boolean,是否从HTTP重定向到HTTPS
  • hostnameVerifier: HostnameVerifier,域名校验
  • eventListenerFactory: EventListener.Factory,Call的生命周期监听器

OkHttpClient中的参数都不是必要的配置项,如果初始化OkHttpClient的时候没有设置则会使用默认配置,而且这些配置会被所有call请求共享。

2. Request类

Request类的职责与OkHttpClient类相似,也是用来配置请求参数的,通过Builder类去配置以下的参数:
url: HttpUrl,请求url
method: String,请求方法,如果没有指定的话会默认配置GET
headers: Headers,请求头
body: RequestBody,请求体
tags: Map<Class<*>, Any>,请求标签 (Retrofit中的请求参数解析为Invocation存到这个hashMap标签里)

3. RealCall类

在OkHttpClient对象调用newCall方法创建Call的时候实际上就会进入到RealCall类中:
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
也就是构建Call的工作是由RealCall去做的,RealCall是连接应用和网络层的桥梁,通过调用RealCall的同步请求execute方法或异步请求enqueue方法发起请求最后拿到Response,下面来分析RealCall类中的主要方法。

3.1 同步请求: execute方法

override fun execute(): Response {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    timeout.enter()
    callStart()
    try {
      // (1)
      client.dispatcher.executed(this)
      // (2) 执行getResponseWithInterceptorChain()并暂存
      return getResponseWithInterceptorChain()
    } finally {
      // (3)执行完finished后, 再返回getResponseWithInterceptorChain()得到的结果
      client.dispatcher.finished(this)
    }
  }

  // Dispatcher.kt
   @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }
  • 检测这个Call是否已经被执行过,一个Call只能被执行一次,如果已经执行过则抛出异常
  • 开启请求超时的计时和开启请求生命周期的监听
  • (1)处: 调用Dispatcher的executed方法将这个RealCall对象加入到正在执行的Call队列(runningSyncCalls)中
  • (2)处: 调用getResponseWithInterceptorChain方法构建拦截器链,遍历拦截器链后返回Response, 具体的请求逻辑在CallServerInterceptor拦截器中实现
  • (3)处: 请求执行完后调用Dispatcher的finish方法将这个Call从队列中移除

3.2 getResponseWithInterceptorChain()方法

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

getResponseWithInterceptorChain方法是整个OkHttp框架执行请求的核心所在,采用了责任链模式:

  • 根据用户自定义的拦截器和OkHttp内置的拦截器按顺序构建出拦截器列表
  • 构建拦截器责任链RealInterceptorChain
  • 调用RealInterceptorChain的proceed方法递归拦截器链,执行请求,直到CallServerInterceptor执行完毕,就是所有拦截器都完成拦截最后返回Response
  • 当请求被取消时关闭响应并抛出异常
3.2.1 拦截器执行方法 RealInterceptorChain.proceed
override fun proceed(request: Request): Response {
    //前置检查 确保当前 index 没有超出拦截器列表范围
    check(index < interceptors.size)

    // 增加调用次数计数器(用于后续验证)
    calls++

    // 网络拦截器(ConnectInterceptor)特殊验证
    if (exchange != null) {
      check(exchange.finder.sameHostAndPort(request.url)) {
        "network interceptor ${interceptors[index - 1]} must retain the same host and port"
      }
      check(calls == 1) {
        "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
      }
    }

    // 调用下一个拦截器
    val next = copy(index = index + 1, request = request)
    val interceptor = interceptors[index]

    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    // 网络拦截器后置验证
    if (exchange != null) {
      check(index + 1 >= interceptors.size || next.calls == 1) {
        "network interceptor $interceptor must call proceed() exactly once"
      }
    }

    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }

RealInterceptorChain.proceed() 方法是 OkHttp 拦截器链的核心方法,负责将请求沿着拦截器链传递并获取响应。下面结合代码和上下文详细解释:

  • 管理拦截器链的执行顺序:通过 index 跟踪当前执行到哪个拦截器
  • 执行拦截器的 intercept 方法:调用当前拦截器并传递修改后的请求
  • 验证网络拦截器的正确使用:对网络拦截器有特殊约束

在 OkHttp 的拦截器链中,递归是通过 拦截器实现中主动调用 chain.proceed() 这一关键操作形成的。让我们深入分析这个递归过程是如何具体实现的:

  1. 自我引用
    chain.proceed()调用拦截器拦截器内又调用 chain.proceed() → 循环这一过程。
  2. 终止条件
    index == interceptors.size 时,最后一个拦截器(通常是 CallServerInterceptor)不再调用 proceed(),递归终止。

3.3 异步请求: enqueue方法

override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

注意: 同步请求execute 最终dispatcher.executed()传入的是当前RealCall对象, 异步请求enqueue最终dispatcher.enqueue()传入的是AsyncCall对象

3.3.1 AsyncCall异步执行

AsyncCall类是定义在RealCall类中的一个内部类 实现了Runnable接口,用于异步执行请求。主要外部调用方法 executeOn

// 尝试将此异步调用加入 executorService 队列中执行。如果线程池已经被关闭,会尝试进行清理工作,并将该调用标记为失败。
fun executeOn(executorService: ExecutorService) {
      client.dispatcher.assertThreadDoesntHoldLock()

      var success = false
      try {
        executorService.execute(this)
        success = true
      } catch (e: RejectedExecutionException) {
        val ioException = InterruptedIOException("executor rejected")
        ioException.initCause(e)
        noMoreExchanges(ioException)
        responseCallback.onFailure(this@RealCall, ioException)
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

executeOn方法被调用在Dispatcher类中enqueue(call: AsyncCall)方法内; executeOn方法主要是通过入参传入一个线程池,并调用这个线程池执行请求,请求失败则抛出异常并将异常通过回调返回去,最后调用Dispatcher的finish方法将这个请求移出队列。
线程池创建在Dispatcher类中,默认维护一个线程池, 可通过DisPatcher的构造函数传入自己定义的线程池对象:

  • 没有常驻核心线程。
  • 所有线程在空闲超过 keepAliveTime(60s)后会被回收
  • 允许线程池创建非常多线程(理论上受限于系统资源)。
  • 非核心线程如果空闲超过 60 秒,将被销毁。
  • 不存储任务。
    每提交一个任务,必须有一个线程立即处理它;否则就会创建一个新线程(直到达到最大限制)。
@get:JvmName("executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        //创建了一个无限线程池
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
      }
      return executorServiceOrNull!!
    }

run()是异步的具体实现:

override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        timeout.enter()
        try {
            // 同 同步请求3.2
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
          } else {
            responseCallback.onFailure(this@RealCall, e)
          }
        } catch (t: Throwable) {
          cancel()
          if (!signalledCallback) {
            val canceledException = IOException("canceled due to $t")
            canceledException.addSuppressed(t)
            responseCallback.onFailure(this@RealCall, canceledException)
          }
          throw t
        } finally {
          client.dispatcher.finished(this)
        }
      }
    }

4. Dispatcher类

class Dispatcher constructor() {

 // 构造函数允许传入自定义线程池对象
  constructor(executorService: ExecutorService) : this() {
    this.executorServiceOrNull = executorService
  }

  @get:Synchronized var maxRequests = 64
  @get:Synchronized var maxRequestsPerHost = 5

  @set:Synchronized
  @get:Synchronized
  var idleCallback: Runnable? = null

  @get:Synchronized
  @get:JvmName("executorService") val executorService: ExecutorService

  private val readyAsyncCalls = ArrayDeque<AsyncCall>()
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()
  private val runningSyncCalls = ArrayDeque<RealCall>()

  // 同步请求加入同步请求队列
  @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

  // 异步请求
  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      // 异步请求加入准备好的异步请求队列
      readyAsyncCalls.add(call)

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.call.forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    // 执行异步请求
    promoteAndExecute()
  }
  ......
}

Dispatcher中定义了以下的成员变量:

  • maxRequests:并行的最大的请求数为64个
  • maxRequestsPerHost:同一个host并行的最大请求数为5个
  • idleCallback:分发器空闲时的回调
  • executorService:线程池
  • readyAsyncCalls:已准备好的异步请求队列
  • runningAsyncCalls:正在执行的异步请求队列,包含已经被取消但还没完成的AsyncCall
  • runningSyncCalls:正在执行的同步请求队列,包含已经被取消但还没完成的RealCall

除了这些成员变量还定义了一些用来调度Call的方法,也就是说Dispatcher的主要职责就是用来调度Call对象的,内部维护了一个线程池和一些同步/异步请求队列,用于存放和调度Call。

5. 总结

请求的时候OkHttpClient会通过newCall方法去访问RealCall,如果是发起同步请求则会生成一个RealCall对象通过execute方法传给分发器Dispatcher处理,如果是异步请求则会传入一个AsyncCall对象传给Dispatcher,无论是同步请求还是异步请求最终都是通过getResponseWithInterceptorChain方法去遍历拦截器链获取Response,下面是OkHttp的简单请求流程:

[图片上传失败...(image-2505ad-1750924619470)]

二. 默认拦截器解析

1. RetryAndFollowUpInterceptor: 处理认证挑战(401/407)和HTTP重定向(3xx)

  • HTTP 重定向 (3xx): 自动跟随新的 Location 头继续请求(默认最多 20 次,防止循环)
  • 认证挑战 (401/407): 若配置了 Authenticator,会自动重试请求(如刷新 Token)
  • 连接失败: 对 幂等请求(GET、HEAD 等)可能自动重试(非幂等请求如 POST 默认不重试)

interceptors += RetryAndFollowUpInterceptor(client)

interceptor方法逐行解析:

override fun intercept(chain: Interceptor.Chain): Response {
    // 将通用Chain转换为RealInterceptorChain以访问更多功能
    val realChain = chain as RealInterceptorChain
  
    // 获取当前请求对象(可能会在重试/重定向过程中被修改)
    var request = chain.request
  
    // 获取当前Call对象,用于控制请求生命周期
    val call = realChain.call
  
    // 跟踪重定向/重试次数的计数器
    var followUpCount = 0
  
    // 存储前一个响应(用于构建响应链)
    var priorResponse: Response? = null
  
    // 标志是否需要创建新的ExchangeFinder(用于寻找连接路由)
    var newExchangeFinder = true
  
    // 主循环,处理重试和重定向逻辑
    while (true) {
        // (1)
        // 进入网络拦截器交换阶段,准备网络请求
        call.enterNetworkInterceptorExchange(request, newExchangeFinder)
  
        var response: Response
        var closeActiveExchange = true  // 标记是否需要关闭当前Exchange
  
        try {
            // 检查请求是否已被取消
            if (call.isCanceled()) {
                throw IOException("Canceled")
            }

            try {
                // (2)
                // 将请求传递给下一个拦截器(实际执行网络请求)
                response = realChain.proceed(request)
                newExchangeFinder = true  // 成功执行后重置标志
            } catch (e: RouteException) {
                // (3)
                // 路由连接失败(请求尚未发送)
                if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
                    throw e.firstConnectException  // 无法恢复则抛出异常
                }
                newExchangeFinder = false  // 重试时不创建新的ExchangeFinder
                continue  // 重试请求
            } catch (e: IOException) {
                //(4)
                // 与服务器通信失败(请求可能已发送)
                if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
                    throw e  // 无法恢复则抛出异常
                }
                newExchangeFinder = false  // 重试时不创建新的ExchangeFinder
                continue  // 重试请求
            }

            // 如果有前一个响应,将其附加到当前响应中(不包含body)
            if (priorResponse != null) {
                response = response.newBuilder()
                    .priorResponse(priorResponse.newBuilder()
                        .body(null)
                        .build())
                    .build()
            }

            // (5)
            // 获取当前的Exchange对象(代表实际的HTTP交换)
            val exchange = call.interceptorScopedExchange
  
            // (6) 判断是否需要重定向
            // 检查响应是否需要跟进(如重定向或认证)
            val followUp = followUpRequest(response, exchange)
            // 如果不需要跟进,处理结束
            if (followUp == null) {
                if (exchange != null && exchange.isDuplex) {
                    call.timeoutEarlyExit()  // 处理双工连接的特殊情况
                }
                closeActiveExchange = false  // 保持连接开启
                return response  // 返回最终响应
            }

            // 检查跟进请求的body是否是"one-shot"(不可重复读取)
            val followUpBody = followUp.body
            if (followUpBody != null && followUpBody.isOneShot()) {
                closeActiveExchange = false  // 保持连接开启
                return response  // 返回当前响应
            }

            // 关闭当前响应体以释放资源
            response.body?.closeQuietly()

            // 检查重定向/跟进次数是否超过限制(默认20次)
            if (++followUpCount > MAX_FOLLOW_UPS) {
                throw ProtocolException("Too many follow-up requests: $followUpCount")
            }

            // 准备下一次迭代:更新请求和priorResponse
            request = followUp
            priorResponse = response
        } finally {
            // 退出网络拦截器交换,根据需要关闭Exchange
            call.exitNetworkInterceptorExchange(closeActiveExchange)
        }
    }
}
  • (1)处如果newExchangeFinder为true,RealCall的enterNetworkInterceptorExchange方法内部会构建一个ExchangeFinder,这个ExchangeFinder是获取连接的工具,后面的连接拦截器ConnectInterceptor会用到; ExchangeFinder 负责寻找可用的连接路由(包括连接池查找、新连接创建等);
    • 路由失败后的重试:
      newExchangeFinder = false
      复用现有 ExchangeFinder 但尝试不同路由
    • 重定向后的新请求:
      newExchangeFinder = true
      必须创建新的查找器(因为目标地址可能已改变)
      示例:收到 302 重定向到新 URL
      示例:某个 IP 连接失败后尝试其他 IP
  • (2)处调用RealInterceptorChain的proceed方法启动下一个拦截器,如果是后面的任意一个拦截器抛出了异常都会在这里捕获到,然后进入下面的catch判断是否尝试重连
  • (3)当路由连接失败捕获到RouteException的时候就会进入⑶处,这时请求还没发出去,会进入recover方法中判断此异常能不能恢复,如果能恢复的话就会记录这次的异常信息,并进行重试
  • (4)当与服务器通信失败捕获到IOException的时候就会进入⑷处,这时请求有可能已经发出去了,同样是跟⑶处一样会调用recover方法,返回true则记录异常并重试
  • (5)获取RealCall的interceptorScopedExchange,这个Exchange会在连接拦截器ConnectInterceptor进行拦截的时候创建,在ConnectInterceptor启动之前Exchange会是null
  • (6)处调用followUpRequest方法进行重定向并获取重定向返回的Request实例followUp,这个followUp将作为下一次重试的Request,而followUpRequest方法中会根据responseCode来判断是否需要进行重定向,如果返回的followUp不为空的话则说明进行了重定向处理,否则说明不需要重定向,结束本次请求直接返回Response

1.1 recover方法--判断异常是否可恢复

源码注释:

private fun recover(
    e: IOException,
    call: RealCall,
    userRequest: Request,
    requestSendStarted: Boolean
  ): Boolean {
    // The application layer has forbidden retries.
    if (!client.retryOnConnectionFailure) return false

    // We can't send the request body again.
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false

    // This exception is fatal.
    if (!isRecoverable(e, requestSendStarted)) return false

    // No more routes to attempt.
    if (!call.retryAfterFailure()) return false

    // For failure recovery, use the same route selector with a new connection.
    return true
  }
  • 用户配置OkHttpClient的时候设置了retryOnConnectionFailure为false(默认配置是true),即用户手动关闭了重试机制,连接失败的时候不允许重试
  • 请求已经发送出去了并且只允许发送一次
  • isRecoverable方法判断为不可重试的异常类型,点进去isRecoverable方法里,具体判断条件如下:
private fun isRecoverable(e: IOException, requestSendStarted: Boolean): Boolean {
    // ProtocolException 协议异常不重试
    if (e is ProtocolException) {
      return false
    }

    // InterruptedIOException中断异常不重试
    if (e is InterruptedIOException) {
      return e is SocketTimeoutException && !requestSendStarted
    }

    // SSLHandshakeException异常不重试
    if (e is SSLHandshakeException) {
      if (e.cause is CertificateException) {
        return false
      }
    }
    // SSLPeerUnverifiedException异常不重试
    if (e is SSLPeerUnverifiedException) {
      // e.g. a certificate pinning error.
      return false
    }
  
    return true
  }

1.2 followUpRequest方法--检查响应是否需要跟进(如重定向或认证)

源码如下:

private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
    // 获取当前连接的路由信息(可能为null)
    val route = exchange?.connection?.route()
  
    // 获取HTTP响应状态码
    val responseCode = userResponse.code

    // 获取原始请求的HTTP方法
    val method = userResponse.request.method
  
    // 根据不同的状态码决定如何处理
    when (responseCode) {
      // 案例1: 407 Proxy Authentication Required (需要代理认证)
      HTTP_PROXY_AUTH -> {
        // 检查当前是否确实使用了HTTP代理
        val selectedProxy = route!!.proxy
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          // 如果不是HTTP代理却收到407,抛出协议异常
          throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
        }
        // 使用配置的代理认证器生成认证请求
        return client.proxyAuthenticator.authenticate(route, userResponse)
      }

      // 案例2: 401 Unauthorized (需要认证)
      HTTP_UNAUTHORIZED -> 
        // 使用配置的认证器生成认证请求
        return client.authenticator.authenticate(route, userResponse)

      // 案例3: 308 Permanent Redirect 或 307 Temporary Redirect
      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT -> {
        // RFC规范要求:对于非GET/HEAD方法的307/308响应,不能自动重定向
        if (method != "GET" && method != "HEAD") {
          return null
        }
        // 构建重定向请求
        return buildRedirectRequest(userResponse, method)
      }

      // 案例4: 300 Multiple Choices, 301 Moved Permanently, 302 Found, 303 See Other
      HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
        // 这些状态码总是可以构建重定向请求
        return buildRedirectRequest(userResponse, method)
      }

      // 案例5: 408 Request Timeout
      HTTP_CLIENT_TIMEOUT -> {
        // 408在实践中很少见,但某些服务器(如HAProxy)会使用
      
        // 检查客户端是否允许重试
        if (!client.retryOnConnectionFailure) {
          return null
        }

        // 检查请求体是否是一次性的(不可重试)
        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }
      
        // 检查是否是连续超时(避免无限重试)
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
          return null
        }

        // 检查Retry-After头是否要求延迟
        if (retryAfter(userResponse, 0) > 0) {
          return null
        }

        // 满足所有条件,返回原始请求进行重试
        return userResponse.request
      }

      // 案例6: 503 Service Unavailable
      HTTP_UNAVAILABLE -> {
        // 检查是否是连续503错误
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
          return null
        }

        // 检查是否有立即重试的指令(Retry-After: 0)
        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
          return userResponse.request
        }

        return null
      }

      // 案例7: 421 Misdirected Request
      HTTP_MISDIRECTED_REQUEST -> {
        // OkHttp可能合并了不同域名的HTTP/2连接,当服务器返回421时,
        // 可以在不同连接上重试
      
        // 检查请求体是否是一次性的
        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }

        // 检查是否是连接合并导致的问题
        if (exchange == null || !exchange.isCoalescedConnection) {
          return null
        }

        // 标记该连接不再合并
        exchange.connection.noCoalescedConnections()
        // 返回原始请求进行重试
        return userResponse.request
      }

      // 默认情况:不需要跟进请求
      else -> return null
    }
  }

2. BridgeInterceptor: 桥接应用层与网络层,添加必要的请求头

interceptors += BridgeInterceptor(client.cookieJar)
BridgeInterceptor 是 OkHttp 中负责桥接应用代码与网络请求的关键拦截器,主要工作是添加必要的请求头和处理响应转换。

3. CacheInterceptor: 处理 HTTP 缓存

interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor

3.1 源码逐行注释

override fun intercept(chain: Interceptor.Chain): Response {
    // 1. 从缓存中获取候选响应(可能为null)
    val cacheCandidate = cache?.get(chain.request())

    // 2. 获取当前时间戳用于缓存策略计算
    val now = System.currentTimeMillis()

    // 3. 计算缓存策略(核心决策逻辑)
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    // 策略决定是否需要网络请求
    val networkRequest = strategy.networkRequest
    // 策略决定是否可以使用缓存响应
    val cacheResponse = strategy.cacheResponse

    // 4. 跟踪缓存策略决策结果(用于监控/统计)
    cache?.trackResponse(strategy)

    // 5. 清理不适用的缓存候选
    if (cacheCandidate != null && cacheResponse == null) {
        // 缓存候选不适用,关闭其响应体释放资源
        cacheCandidate.body?.closeQuietly()
    }

    // 6. 处理既不能使用网络也不能使用缓存的情况
    if (networkRequest == null && cacheResponse == null) {
        // 返回504网关超时错误(当设置了only-if-cached且无可用缓存时)
        return Response.Builder()
            .request(chain.request())
            .protocol(Protocol.HTTP_1_1)
            .code(HTTP_GATEWAY_TIMEOUT)
            .message("Unsatisfiable Request (only-if-cached)")
            .body(EMPTY_RESPONSE)
            .sentRequestAtMillis(-1L)  // 表示未发送实际请求
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build()
    }

    // 7. 如果不需要网络请求,直接返回缓存响应
    if (networkRequest == null) {
        return cacheResponse!!.newBuilder()
            .cacheResponse(stripBody(cacheResponse))  // 剥离嵌套响应的body
            .build()
    }

    // 8. 需要网络请求的情况
    var networkResponse: Response? = null
    try {
        // 执行网络请求
        networkResponse = chain.proceed(networkRequest)
    } finally {
        // 9. 确保网络请求失败时不会泄漏缓存资源
        if (networkResponse == null && cacheCandidate != null) {
            cacheCandidate.body?.closeQuietly()
        }
    }

    // 10. 处理条件GET情况(缓存验证)
    if (cacheResponse != null) {
        // 如果服务器返回304 Not Modified
        if (networkResponse?.code == HTTP_NOT_MODIFIED) {
            // 合并缓存响应和网络响应的headers
            val response = cacheResponse.newBuilder()
                .headers(combine(cacheResponse.headers, networkResponse.headers))
                .sentRequestAtMillis(networkResponse.sentRequestAtMillis)
                .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
                .cacheResponse(stripBody(cacheResponse))  // 嵌套的缓存响应
                .networkResponse(stripBody(networkResponse))  // 嵌套的网络响应
                .build()

            // 关闭网络响应的body(304响应没有body)
            networkResponse.body!!.close()

            // 更新缓存(在合并headers后,但在处理Content-Encoding前)
            cache!!.trackConditionalCacheHit()  // 跟踪缓存命中
            cache.update(cacheResponse, response)  // 更新缓存条目
            return response
        } else {
            // 不是304响应,关闭旧的缓存响应体
            cacheResponse.body?.closeQuietly()
        }
    }

    // 11. 构建最终响应
    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))  // 嵌套的缓存响应(可能为null)
        .networkResponse(stripBody(networkResponse))  // 嵌套的网络响应
        .build()

    // 12. 缓存处理
    if (cache != null) {
        // 如果响应可缓存
        if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
            // 尝试将响应写入缓存
            val cacheRequest = cache.put(response)
            return cacheWritingResponse(cacheRequest, response)  // 返回带缓存写入功能的响应
        }

        // 处理使缓存无效的HTTP方法(如POST/PUT/DELETE等)
        if (HttpMethod.invalidatesCache(networkRequest.method)) {
            try {
                cache.remove(networkRequest)  // 移除相关缓存
            } catch (_: IOException) {
                // 缓存写入失败(忽略不影响主流程)
            }
        }
    }

    // 13. 返回最终响应
    return response
}

3.2 缓存策略关键点

  1. 缓存决策流程:

    • 检查请求是否允许缓存
    • 检查缓存是否存在且有效
    • 检查缓存是否需要验证(条件GET)
    • 决定使用网络、缓存或两者结合
  2. 条件GET处理:

    • 当缓存过期但可能仍有效时
    • 发送带 If-Modified-SinceIf-None-Match头的请求
    • 处理304 Not Modified响应
  3. 缓存验证头:

    • Last-Modified + If-Modified-Since
    • ETag + If-None-Match
  4. 缓存更新机制:

    • 304响应时合并headers更新缓存
    • 新响应时完整写入缓存
    • 对非GET请求使相关缓存失效

这个拦截器实现了完整的HTTP缓存语义(RFC 7234),包括缓存存储、检索、验证和失效的全生命周期管理。

3.3 缓存策略 CacheStrategy

CacheStrategy.Factory 的 compute() 方法会返回包含两个字段的对象:

class CacheStrategy internal constructor(
  val networkRequest: Request?,  // 需要发送的网络请求(null表示不使用网络)
  val cacheResponse: Response?   // 可用的缓存响应(null表示不使用缓存)
)

决策因素

  1. 请求缓存控制

    • noCache:是否跳过缓存
    • onlyIfCached:是否只使用缓存
    • maxAge:可接受的最大缓存年龄
  2. 响应缓存控制

    • mustRevalidate:是否必须重新验证
    • maxAge:缓存新鲜时间
    • expires:过期时间
  3. 缓存有效性

    • 是否有缓存响应
    • 缓存响应是否完整
    • 缓存是否过期
  4. HTTP方法

    • GET请求通常可缓存
    • POST等非幂等方法通常不可缓存

源码关键逻辑

fun compute(): CacheStrategy {
    // 1. 检查请求是否强制不使用缓存
    val candidate = if (cacheResponse != null) {
        if (request.cacheControl.noCache || cacheResponse.cacheControl.noCache) {
            return CacheStrategy(request, null) // 必须使用网络
        }
      
        // 2. 检查缓存新鲜度
        val ageMillis = cacheAgeMillis()
        val freshMillis = cacheFreshnessLifetime()
      
        if (ageMillis < freshMillis) {
            // 缓存仍新鲜
            if (!request.cacheControl.noCache) {
                return CacheStrategy(null, cacheResponse) // 直接使用缓存
            }
        }
      
        // 3. 检查是否需要验证
        if (cacheResponse.cacheControl.mustRevalidate 
            || request.cacheControl.mustRevalidate
            || ageMillis > freshMillis) {
          
            // 需要条件GET验证
            val conditionalRequest = buildConditionalRequest()
            return CacheStrategy(conditionalRequest, cacheResponse)
        }
      
        // 4. 其他情况处理...
    }
  
    // 5. 无可用缓存或必须使用网络
    return CacheStrategy(request, null)
}

3.4 在OkHttp中实现指定域名缓存的方法

方法一:自定义CacheControl策略

val client = OkHttpClient.Builder()
    .cache(Cache(File("cacheDir"), 10 * 1024 * 1024) // 10MB缓存
    .addNetworkInterceptor { chain ->
        val request = chain.request()
        val host = request.url.host
      
        // 只对特定域名启用缓存
        val cacheControl = if (host == "example.com" || host == "api.example.com") {
            CacheControl.FORCE_NETWORK // 或者自定义缓存策略
        } else {
            CacheControl.Builder().noStore().build() // 禁用缓存
        }
      
        val newRequest = request.newBuilder()
            .cacheControl(cacheControl)
            .build()
      
        chain.proceed(newRequest)
    }
    .build()

方法二:自定义Cache实现

class DomainAwareCache(delegate: Cache, private val allowedDomains: Set<String>) : Cache by delegate {
    override fun get(request: Request): Response? {
        return if (request.url.host in allowedDomains) {
            delegate.get(request)
        } else {
            null
        }
    }

    override fun put(response: Response): CacheRequest? {
        return if (response.request.url.host in allowedDomains) {
            delegate.put(response)
        } else {
            null
        }
    }
}

// 使用方式
val cache = DomainAwareCache(
    delegate = Cache(File("cacheDir"), 10 * 1024 * 1024),
    allowedDomains = setOf("example.com", "api.example.com")
)
val client = OkHttpClient.Builder().cache(cache).build()

方法三:拦截器+缓存策略组合

class DomainCacheInterceptor(private val cachableDomains: Set<String>) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val host = originalRequest.url.host
      
        // 修改缓存策略
        val newRequest = if (host in cachableDomains) {
            originalRequest.newBuilder()
                .cacheControl(CacheControl.Builder()
                    .maxAge(30, TimeUnit.MINUTES)
                    .build())
                .build()
        } else {
            originalRequest.newBuilder()
                .cacheControl(CacheControl.Builder()
                    .noStore()
                    .build())
                .build()
        }
      
        return chain.proceed(newRequest)
    }
}

// 使用方式
val client = OkHttpClient.Builder()
    .cache(Cache(File("cacheDir"), 10 * 1024 * 1024))
    .addNetworkInterceptor(DomainCacheInterceptor(setOf("example.com")))
    .build()

4. ConnectInterceptor: 建立与目标服务器的连接

4.1 intercept源码

// 拦截方法,throws声明可能抛出IO异常
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    // 1. 将通用Chain转换为RealInterceptorChain以访问OkHttp特有功能
    val realChain = chain as RealInterceptorChain
  
    // 2. 初始化Exchange对象(核心连接建立点)
    // initExchange()方法会:
    // - 查找可用连接或创建新连接
    // - 必要时进行TCP握手和TLS握手
    // - 创建用于读写数据的Exchange对象
    val exchange = realChain.call.initExchange(chain)
  
    // 3. 创建携带新Exchange对象的拦截器链副本
    // 这个exchange将被后续拦截器使用(特别是CallServerInterceptor)
    val connectedChain = realChain.copy(exchange = exchange)
  
    // 4. 将请求传递给下一个拦截器(通常是CallServerInterceptor)
    // 此时连接已建立,后续拦截器可以直接进行网络IO
    return connectedChain.proceed(realChain.request)
  }

关键点解析

  1. initExchange() 核心作用
    • 通过 ExchangeFinder 查找可用连接
    • 处理以下情况:
      • 从连接池复用现有连接
      • 创建全新连接(TCP + TLS握手)
      • 处理代理配置
      • 处理HTTP/2或HTTP/1.x协议协商
    • 返回的 Exchange 对象封装了实际的网络IO能力

  1. 连接建立流程
    graph TD
      A[开始] --> B{连接池有可用连接?}
      B -->|是| C[复用连接]
      B -->|否| D[DNS解析]
      D --> E[建立TCP连接]
      E --> F{需要TLS?}
      F -->|是| G[TLS握手]
      F -->|否| H[完成]
      G --> H
      C --> H
    

  1. Exchange 对象职责

    • 管理底层Socket连接
    • 创建 ExchangeCodec(用于编码请求/解码响应)
    • 处理连接超时和IO操作
    • 支持HTTP/2的流管理和优先级
  2. 设计特点

    • 延迟连接:直到需要发送请求体时才真正建立连接
    • 连接复用:通过 ConnectionPool 最大化连接利用率
    • 超时控制:集成OkHttp的超时机制
    • 透明升级:自动处理HTTP/1.1到HTTP/2的升级

异常处理

该方法可能抛出以下类型异常:

  • IOException:网络连接问题(如超时、DNS解析失败等)
  • RouteException:特定路由连接失败(会触发重试其他路由)
  • SSLHandshakeException:TLS握手失败

性能考虑

  1. 连接池化:减少重复建立连接的开销
  2. 延迟创建:直到必要时才建立昂贵资源
  3. 多路复用:HTTP/2下共享同一连接
  4. 自动重试:对某些可恢复错误自动尝试备用路由

这个拦截器虽然代码量少,但却是OkHttp网络通信最核心的组件之一,负责将抽象的请求转换为实际的网络连接。

5. Network Interceptors: 观察和修改网络层的请求和响应

由用户通过 OkHttpClient.Builder 添加,作用:观察和修改网络层的请求和响应

if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }

6. CallServerInterceptor: 向服务器发送请求并读取响应

6.1 interceptor源码解析

interceptors += CallServerInterceptor(forWebSocket)
CallServerInterceptor 是 OkHttp 拦截器链中最后一个拦截器,负责实际的网络请求发送和响应接收。
核心工作:

  • 通过 ExchangeCodec 写入请求头和请求体
  • 如果是 Expect: 100-continue 请求,先等待服务器响应
  • 读取响应头和响应体
  • 处理分块传输编码(chunked transfer encoding)
  • 关闭请求和响应资源
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    // 转换为RealInterceptorChain以访问OkHttp特有功能
    val realChain = chain as RealInterceptorChain
    // 获取前一个拦截器(ConnectInterceptor)创建的Exchange对象
    val exchange = realChain.exchange!!
    // 获取当前请求对象
    val request = realChain.request
    // 获取请求体(可能为null)
    val requestBody = request.body
    // 记录请求发送开始时间
    val sentRequestMillis = System.currentTimeMillis()

    // 1. 写入请求头
    exchange.writeRequestHeaders(request)

    var invokeStartEvent = true  // 是否触发响应头开始事件
    var responseBuilder: Response.Builder? = null  // 响应构建器

    // 2. 处理可能有请求体的方法(POST/PUT等)
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // 处理"Expect: 100-continue"场景
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        // 先刷新请求头,等待服务器返回100 Continue
        exchange.flushRequest()
        // 读取响应头(期待100 Continue)
        responseBuilder = exchange.readResponseHeaders(expectContinue = true)
        exchange.responseHeadersStart()  // 触发响应头开始事件
        invokeStartEvent = false
      }

      if (responseBuilder == null) {
        // 没有100-continue要求或已收到继续指令
        if (requestBody.isDuplex()) {
          // 处理双工请求体(可以边读边写)
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // 普通请求体写入
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()  // 关闭请求体
        }
      } else {
        // 100-continue期望未满足(如收到4xx响应)
        exchange.noRequestBody()  // 标记无请求体
        if (!exchange.connection.isMultiplexed) {
          // 对于HTTP/1.1,防止连接重用(因为状态不一致)
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      // 无请求体的方法(GET/HEAD等)
      exchange.noRequestBody()
    }

    // 3. 完成请求(非双工请求)
    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()
    }

    // 4. 读取响应头
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()  // 触发响应头开始事件
        invokeStartEvent = false
      }
    }

    // 5. 构建初始响应对象
    var response = responseBuilder
        .request(request)  // 关联原始请求
        .handshake(exchange.connection.handshake())  // TLS握手信息
        .sentRequestAtMillis(sentRequestMillis)  // 请求发送时间
        .receivedResponseAtMillis(System.currentTimeMillis())  // 响应接收时间
        .build()

    // 6. 处理100 Continue特殊情况
    var code = response.code
    if (code == 100) {
      // 服务器返回了未预期的100 Continue,继续读取实际响应
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
      }
      response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      code = response.code
    }

    // 7. 响应头读取完成事件
    exchange.responseHeadersEnd(response)

    // 8. 处理响应体
    response = if (forWebSocket && code == 101) {
      // WebSocket升级响应(101 Switching Protocols)
      response.newBuilder()
          .body(EMPTY_RESPONSE)  // 使用空响应体
          .build()
    } else {
      // 普通响应,打开响应体流
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }

    // 9. 处理连接关闭头
    if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
        "close".equals(response.header("Connection"), ignoreCase = true)) {
      exchange.noNewExchangesOnConnection()  // 标记连接不再重用
    }

    // 10. 验证无内容响应的Content-Length
    if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
      throw ProtocolException(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }

    return response
  }
}

6.2 Exchange 在整个okhttp请求链路中的作用是

在 OkHttp 的请求链路中,[exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 是一个非常关键的对象,它贯穿了网络请求的实际执行过程。下面是 [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 的主要作用:


1. 承载网络连接和请求的上下文

  • [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 是 Exchange 类型对象,用于管理一次 HTTP 请求/响应交换过程
  • 它持有一个底层的网络连接(如 HTTP/1.x 或 HTTP/2 的流),并通过该连接进行数据读写。

2. 控制请求的发送与响应的接收

  • 所有通过 NetworkInterceptor 和最终的网络调用发起的请求,都是通过 [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 发送出去的。
  • 它负责:
    • 写出请求头和请求体
    • 读取响应头和响应体

3. 支持多个请求复用同一个连接(HTTP Keep-Alive)

  • [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 能够与连接池配合,实现连接复用,提升性能。
  • 如果当前连接可以复用(例如 HTTP/1.1 Keep-Alive 或 HTTP/2),[exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 会复用已有的连接来发送新的请求。

4. 保障拦截器链的一致性和正确性
在 [RealInterceptorChain](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/http/RealInterceptorChain.kt#L35-L112) 中,[exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 用于:

  • 校验请求是否保持相同的 host 和 portnetwork 拦截器中,如果修改了请求的目标地址,OkHttp 会抛出异常。
  • 确保每个拦截器只调用一次 [proceed()](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/http/RealInterceptorChain.kt#L79-L111)
    防止拦截器中多次调用 chain.proceed() 导致的行为不一致或资源泄漏。

5. 传递超时设置
虽然超时配置由 [RealInterceptorChain](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/http/RealInterceptorChain.kt#L35-L112) 管理,但 [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 在实际执行网络 I/O 时会使用这些超时参数。


总结:[exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320) 的核心作用

功能 描述
📡 请求/响应传输 实际发送 HTTP 请求并接收响应
🔗 连接管理 复用底层 TCP/HTTP2 连接,提高效率
✅ 拦截器校验 确保网络拦截器行为合规(host 不变、仅调用一次 proceed)
⏱️ 超时控制 使用链路中的超时设置执行网络 I/O
🧬 流程协调 协助拦截器链完成责任链模式下的流程控制

示例说明

val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
val response = interceptor.intercept(next) // exchange 会在后续被使用,比如 ConnectInterceptor 或 CallServerInterceptor

在 [ConnectInterceptor](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/connection/ConnectInterceptor.kt#L27-L35) 中会创建 [exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/Response.kt#L320-L320),在 [CallServerInterceptor](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/http/CallServerInterceptor.kt#L25-L129) 中使用它来真正地写出请求和读取响应。


如需更深入了解 [Exchange](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/connection/Exchange.kt#L39-L326) 的实现,可参考其源码文件路径:
[file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/connection/Exchange.kt](file:///Volumes/mjwang/code/okhttp/okhttp/src/main/java/okhttp3/internal/connection/Exchange.kt)

三. ExchangeFinder、ExchangeCodec 和 Exchange 三者的关系解析

在 OkHttp 的网络通信架构中,ExchangeFinderExchangeCodecExchange 这三个类共同协作完成网络连接的查找、建立和数据交换工作。以下是它们的详细关系和职责划分:

1. 核心职责对比

类名 主要职责 生命周期
ExchangeFinder 负责查找/建立物理连接(TCP+TLS),处理连接池复用、路由选择等 每个Call周期
ExchangeCodec 定义HTTP协议编码/解码的抽象接口,处理实际的请求写入和响应读取 单个请求-响应周期
Exchange 协调Finder和Codec,管理单个请求-响应交换的全过程,提供超时和事件通知 单个请求-响应周期

2. 协作关系图解

sequenceDiagram
    participant C as Call
    participant EF as ExchangeFinder
    participant E as Exchange
    participant EC as ExchangeCodec
    participant CP as ConnectionPool
  
    C->>EF: find() 
    EF->>CP: 获取可用连接
    CP-->>EF: 返回连接(或null)
    alt 无可用连接
        EF->>EF: 创建新连接(TCP+TLS)
    end
    EF->>E: 创建Exchange
    E->>EC: 创建ExchangeCodec
    C->>E: 写入请求
    E->>EC: encodeRequest()
    EC->>Socket: 写数据
    C->>E: 读取响应
    E->>EC: readResponse()
    EC->>Socket: 读数据
    E-->>C: 返回Response

3. 详细组件解析

3.1 ExchangeFinder(连接查找器)

核心功能

  • 通过 findConnection() 方法实现连接查找策略:
    fun find(): Exchange {
        // 1. 尝试复用已存在的连接
        // 2. 尝试从连接池获取可用连接
        // 3. 选择路由(可能触发DNS查询)
        // 4. 建立新连接(TCP+TLS握手)
        // 5. 必要时进行HTTP/2连接前置测试
    }
    
  • 处理以下复杂场景:
    • 连接池复用(相同Address的连接)
    • HTTP/2连接多路复用
    • 失败路由的黑名单管理
    • 代理配置处理

关键设计

  • 实现了 RouteSelector 进行路由选择
  • ConnectionPool 紧密交互
  • 支持透明健康检查

3.2 ExchangeCodec(协议编解码器)

实现类

  • Http1ExchangeCodec:HTTP/1.1协议实现
  • Http2ExchangeCodec:HTTP/2协议实现

核心方法

interface ExchangeCodec {
    fun writeRequestHeaders(request: Request)  // 写入请求头
    fun createRequestBody(request: Request): Sink  // 创建请求体Sink
    fun readResponseHeaders(): Response.Builder?  // 读取响应头
    fun openResponseBodySource(response: Response): Source  // 打开响应体Source
}

协议差异处理

特性 HTTP/1.1 HTTP/2
数据流 串行 多路复用
头部压缩 HPACK压缩
连接复用 有限复用 完全多路复用
服务器推送 不支持 支持

3.3 Exchange(交换协调器)

核心职责

  • 组合 ExchangeFinderExchangeCodec 的功能
  • 提供面向拦截器的统一API
  • 管理请求-响应全过程的超时
  • 处理以下事件通知:
    fun responseHeadersStart()
    fun responseHeadersEnd(response: Response)
    fun responseBodyStart()
    

关键代码结构

class Exchange(
    private val finder: ExchangeFinder,
    private val codec: ExchangeCodec,
    private val eventListener: EventListener
) {
    fun writeRequestHeaders(request: Request) {
        codec.writeRequestHeaders(request)
    }
  
    fun createRequestBody(request: Request): Sink {
        return codec.createRequestBody(request).also {
            eventListener.requestBodyStart(call)
        }
    }
}

4. 典型工作流程

  1. 连接建立阶段

    • ExchangeFinder 查找/创建物理连接
    • 根据协议协商结果创建对应的 ExchangeCodec
    • 将两者组合成 Exchange
  2. 请求发送阶段

    • 拦截器调用 Exchange.writeRequestHeaders()
    • Exchange 委托 ExchangeCodec 执行具体协议编码
    • 处理 Expect: 100-continue 等特殊场景
  3. 响应接收阶段

    • 拦截器调用 Exchange.readResponseHeaders()
    • ExchangeCodec 解码原始响应数据
    • Exchange 添加计时信息和事件通知
  4. 资源清理阶段

    • Exchange 负责正确关闭流和连接
    • 将可用连接返回连接池

5. 设计优势

  1. 职责分离

    • Finder专注连接管理,Codec专注协议处理,Exchange负责协调
  2. 协议抽象

    • 通过ExchangeCodec接口隔离HTTP协议版本差异
  3. 资源高效利用

    • 智能的连接复用机制
    • 精确的超时控制
  4. 可扩展性

    • 容易添加新的协议实现(如HTTP/3)
    • 清晰的事件通知系统

这三个组件的协同工作使得OkHttp能够高效、可靠地处理各种网络通信场景,同时保持代码的模块化和可维护性。

6.如果你想

  • 优化连接复用性能 ➜ 理解 ExchangeFinder 和 ConnectionPool
  • 调试协议问题(如 HTTP/2) ➜ 理解 ExchangeCodec
  • 监控请求全过程事件(如上传下载进度) ➜ 理解 Exchange 和 EventListener

相关文章

网友评论

    本文标题:2025 再读Android Okhttp源码

    本文链接:https://www.haomeiwen.com/subject/jhuygjtx.html