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() 这一关键操作形成的。让我们深入分析这个递归过程是如何具体实现的:
- 自我引用
chain.proceed()→调用拦截器→拦截器内又调用 chain.proceed()→ 循环这一过程。 - 终止条件
当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 缓存策略关键点
-
缓存决策流程:
- 检查请求是否允许缓存
- 检查缓存是否存在且有效
- 检查缓存是否需要验证(条件GET)
- 决定使用网络、缓存或两者结合
-
条件GET处理:
- 当缓存过期但可能仍有效时
- 发送带
If-Modified-Since或If-None-Match头的请求 - 处理304 Not Modified响应
-
缓存验证头:
-
Last-Modified+If-Modified-Since -
ETag+If-None-Match
-
-
缓存更新机制:
- 304响应时合并headers更新缓存
- 新响应时完整写入缓存
- 对非GET请求使相关缓存失效
这个拦截器实现了完整的HTTP缓存语义(RFC 7234),包括缓存存储、检索、验证和失效的全生命周期管理。
3.3 缓存策略 CacheStrategy
CacheStrategy.Factory 的 compute() 方法会返回包含两个字段的对象:
class CacheStrategy internal constructor(
val networkRequest: Request?, // 需要发送的网络请求(null表示不使用网络)
val cacheResponse: Response? // 可用的缓存响应(null表示不使用缓存)
)
决策因素
-
请求缓存控制:
-
noCache:是否跳过缓存 -
onlyIfCached:是否只使用缓存 -
maxAge:可接受的最大缓存年龄
-
-
响应缓存控制:
-
mustRevalidate:是否必须重新验证 -
maxAge:缓存新鲜时间 -
expires:过期时间
-
-
缓存有效性:
- 是否有缓存响应
- 缓存响应是否完整
- 缓存是否过期
-
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)
}
关键点解析
-
initExchange()核心作用:- 通过
ExchangeFinder查找可用连接 - 处理以下情况:
- 从连接池复用现有连接
- 创建全新连接(TCP + TLS握手)
- 处理代理配置
- 处理HTTP/2或HTTP/1.x协议协商
- 返回的
Exchange对象封装了实际的网络IO能力
- 通过
-
连接建立流程:
graph TD A[开始] --> B{连接池有可用连接?} B -->|是| C[复用连接] B -->|否| D[DNS解析] D --> E[建立TCP连接] E --> F{需要TLS?} F -->|是| G[TLS握手] F -->|否| H[完成] G --> H C --> H
-
Exchange对象职责:- 管理底层Socket连接
- 创建
ExchangeCodec(用于编码请求/解码响应) - 处理连接超时和IO操作
- 支持HTTP/2的流管理和优先级
-
设计特点:
- 延迟连接:直到需要发送请求体时才真正建立连接
-
连接复用:通过
ConnectionPool最大化连接利用率 - 超时控制:集成OkHttp的超时机制
- 透明升级:自动处理HTTP/1.1到HTTP/2的升级
异常处理
该方法可能抛出以下类型异常:
-
IOException:网络连接问题(如超时、DNS解析失败等) -
RouteException:特定路由连接失败(会触发重试其他路由) -
SSLHandshakeException:TLS握手失败
性能考虑
- 连接池化:减少重复建立连接的开销
- 延迟创建:直到必要时才建立昂贵资源
- 多路复用:HTTP/2下共享同一连接
- 自动重试:对某些可恢复错误自动尝试备用路由
这个拦截器虽然代码量少,但却是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 和 port在
network拦截器中,如果修改了请求的目标地址,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 的网络通信架构中,ExchangeFinder、ExchangeCodec 和 Exchange 这三个类共同协作完成网络连接的查找、建立和数据交换工作。以下是它们的详细关系和职责划分:
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(交换协调器)
核心职责:
- 组合
ExchangeFinder和ExchangeCodec的功能 - 提供面向拦截器的统一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. 典型工作流程
-
连接建立阶段:
-
ExchangeFinder查找/创建物理连接 - 根据协议协商结果创建对应的
ExchangeCodec - 将两者组合成
Exchange
-
-
请求发送阶段:
- 拦截器调用
Exchange.writeRequestHeaders() -
Exchange委托ExchangeCodec执行具体协议编码 - 处理
Expect: 100-continue等特殊场景
- 拦截器调用
-
响应接收阶段:
- 拦截器调用
Exchange.readResponseHeaders() -
ExchangeCodec解码原始响应数据 -
Exchange添加计时信息和事件通知
- 拦截器调用
-
资源清理阶段:
-
Exchange负责正确关闭流和连接 - 将可用连接返回连接池
-
5. 设计优势
-
职责分离:
- Finder专注连接管理,Codec专注协议处理,Exchange负责协调
-
协议抽象:
- 通过ExchangeCodec接口隔离HTTP协议版本差异
-
资源高效利用:
- 智能的连接复用机制
- 精确的超时控制
-
可扩展性:
- 容易添加新的协议实现(如HTTP/3)
- 清晰的事件通知系统
这三个组件的协同工作使得OkHttp能够高效、可靠地处理各种网络通信场景,同时保持代码的模块化和可维护性。
6.如果你想
- 优化连接复用性能 ➜ 理解 ExchangeFinder 和 ConnectionPool
- 调试协议问题(如 HTTP/2) ➜ 理解 ExchangeCodec
- 监控请求全过程事件(如上传下载进度) ➜ 理解 Exchange 和 EventListener













网友评论