OkHttp 源码解析:拦截器链、连接池与缓存策略的设计精髓

OkHttp 源码解析:拦截器链、连接池与缓存策略的设计精髓

OkHttp 是 Android 和 Java 生态中最流行的 HTTP 客户端库,以其高效、简洁的 API 和强大的功能著称。无论是直接使用还是通过 Retrofit 间接调用,OkHttp 都在幕后默默支撑着网络请求的高效执行。

深入理解 OkHttp 的源码,不仅能帮助我们更好地使用它,还能学习到优秀框架的设计思想:责任链模式、连接复用、缓存策略等核心机制。

OkHttp 架构概览

OkHttp 的核心设计围绕三大支柱:

  1. 拦截器链(Interceptor Chain):责任链模式,将请求处理分解为多个独立步骤
  2. 连接池(ConnectionPool):复用 TCP 连接,减少握手开销
  3. 缓存策略(Cache):遵循 HTTP 缓存规范,减少不必要的网络请求

一个请求的生命周期

kotlin 复制代码
val client = OkHttpClient()
val request = Request.Builder()
    .url("https://api.example.com/users")
    .build()

client.newCall(request).execute()

这段简单的代码背后,经历了:

复制代码
创建 RealCall
  ↓
进入拦截器链
  ↓
RetryAndFollowUpInterceptor(重试与重定向)
  ↓
BridgeInterceptor(桥接:添加请求头)
  ↓
CacheInterceptor(缓存策略)
  ↓
ConnectInterceptor(连接建立)
  ↓
CallServerInterceptor(发送请求与接收响应)
  ↓
逐层返回响应

拦截器链:责任链模式的精妙实现

拦截器链的核心逻辑

OkHttp 的拦截器链是典型的责任链模式应用。每个拦截器负责一个独立职责,并决定是否将请求传递给下一个拦截器。

RealInterceptorChain 核心代码简化:

kotlin 复制代码
class RealInterceptorChain(
    private val interceptors: List<Interceptor>,
    private val index: Int,
    private val request: Request,
    // ... 其他参数
) : Interceptor.Chain {

    override fun proceed(request: Request): Response {
        // 检查是否已到达链尾
        if (index >= interceptors.size) throw AssertionError()

        // 创建下一个链节点
        val next = RealInterceptorChain(
            interceptors = interceptors,
            index = index + 1,
            request = request,
            // ...
        )

        // 获取当前拦截器并执行
        val interceptor = interceptors[index]
        val response = interceptor.intercept(next)

        return response
    }
}

设计精髓:

  • 递归调用 proceed(),每次 index + 1
  • 每个拦截器都可以决定是否调用 chain.proceed()
  • 如果不调用 proceed(),链条中断,直接返回响应(如命中缓存)
  • 拦截器可以修改请求(前置)或响应(后置)

内置拦截器解析

1. RetryAndFollowUpInterceptor - 重试与重定向

负责请求失败后的重试和 HTTP 重定向(301/302)。

kotlin 复制代码
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
    
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        var followUpCount = 0
        var priorResponse: Response? = null

        while (true) {
            try {
                val response = chain.proceed(request)
                
                // 检查是否需要重定向
                val followUp = followUpRequest(response, exchange)
                
                if (followUp == null) {
                    // 不需要重定向,返回响应
                    return response
                }
                
                // 检查重定向次数
                if (++followUpCount > MAX_FOLLOW_UPS) {
                    throw ProtocolException("Too many follow-up requests: $followUpCount")
                }
                
                // 准备下一次请求
                request = followUp
                priorResponse = response
                
            } catch (e: IOException) {
                // 判断是否可以重试
                if (!recover(e, call, request, requestSendStarted)) {
                    throw e
                }
            }
        }
    }
    
    private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
        val route = exchange?.connection()?.route()
        val responseCode = userResponse.code

        when (responseCode) {
            HTTP_PROXY_AUTH -> // 代理认证
            HTTP_UNAUTHORIZED -> // 401 认证
            HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT -> // 307/308 重定向
            HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
                // 301/302/303 重定向
                return buildRedirectRequest(userResponse, method)
            }
            HTTP_CLIENT_TIMEOUT -> // 408 超时重试
            HTTP_UNAVAILABLE -> // 503 服务不可用
            else -> return null
        }
    }
}

核心机制:

  • 捕获 IOException,判断是否可重试(网络连接、超时等)
  • 检查响应码,处理 HTTP 重定向(3xx)和认证(401/407)
  • 限制最大重定向次数(默认 20 次)防止死循环
  • 自动处理 Location 响应头构建新请求
2. BridgeInterceptor - 桥接拦截器

作为应用代码和网络代码之间的桥梁,补充必要的 HTTP 请求头。

kotlin 复制代码
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
    
    override fun intercept(chain: Interceptor.Chain): Response {
        val userRequest = chain.request()
        val requestBuilder = userRequest.newBuilder()

        // 补充请求头
        val body = userRequest.body
        if (body != null) {
            val contentType = body.contentType()
            if (contentType != null) {
                requestBuilder.header("Content-Type", contentType.toString())
            }

            val contentLength = body.contentLength()
            if (contentLength != -1L) {
                requestBuilder.header("Content-Length", contentLength.toString())
                requestBuilder.removeHeader("Transfer-Encoding")
            } else {
                requestBuilder.header("Transfer-Encoding", "chunked")
                requestBuilder.removeHeader("Content-Length")
            }
        }

        if (userRequest.header("Host") == null) {
            requestBuilder.header("Host", userRequest.url.toHostHeader())
        }

        if (userRequest.header("Connection") == null) {
            requestBuilder.header("Connection", "Keep-Alive")
        }

        // 支持 gzip 压缩
        var transparentGzip = false
        if (userRequest.header("Accept-Encoding") == null && 
            userRequest.header("Range") == null) {
            transparentGzip = true
            requestBuilder.header("Accept-Encoding", "gzip")
        }

        // 加载 Cookie
        val cookies = cookieJar.loadForRequest(userRequest.url)
        if (cookies.isNotEmpty()) {
            requestBuilder.header("Cookie", cookieHeader(cookies))
        }

        if (userRequest.header("User-Agent") == null) {
            requestBuilder.header("User-Agent", userAgent)
        }

        // 发送请求
        val networkResponse = chain.proceed(requestBuilder.build())

        // 保存 Cookie
        cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

        // 处理 gzip 解压
        if (transparentGzip && "gzip".equals(networkResponse.header("Content-Encoding"), true)) {
            val responseBody = networkResponse.body
            if (responseBody != null) {
                val gzipSource = GzipSource(responseBody.source())
                val strippedHeaders = networkResponse.headers.newBuilder()
                    .removeAll("Content-Encoding")
                    .removeAll("Content-Length")
                    .build()
                return networkResponse.newBuilder()
                    .headers(strippedHeaders)
                    .body(RealResponseBody(strippedHeaders, Okio.buffer(gzipSource)))
                    .build()
            }
        }

        return networkResponse
    }
}

职责:

  • 补充 Content-Type、Content-Length、Host、Connection 等必要头
  • 自动处理 gzip 压缩和解压
  • 管理 Cookie 的保存和加载
  • 默认添加 User-Agent
3. CacheInterceptor - 缓存拦截器

实现 HTTP 缓存规范,决定是否使用缓存、是否发起网络请求。

kotlin 复制代码
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
    
    override fun intercept(chain: Interceptor.Chain): Response {
        val cacheCandidate = cache?.get(chain.request())

        val now = System.currentTimeMillis()
        
        // 缓存策略工厂:根据请求和缓存响应计算策略
        val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
        val networkRequest = strategy.networkRequest
        val cacheResponse = strategy.cacheResponse

        cache?.trackResponse(strategy)

        if (cacheCandidate != null && cacheResponse == null) {
            // 缓存不可用,关闭
            cacheCandidate.body?.closeQuietly()
        }

        // 如果不允许使用网络且没有缓存,返回 504
        if (networkRequest == null && cacheResponse == null) {
            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()
        }

        // 如果不需要网络请求,直接返回缓存
        if (networkRequest == null) {
            return cacheResponse!!.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .build()
        }

        // 发起网络请求
        var networkResponse: Response? = null
        try {
            networkResponse = chain.proceed(networkRequest)
        } finally {
            if (networkResponse == null && cacheCandidate != null) {
                cacheCandidate.body?.closeQuietly()
            }
        }

        // 如果有缓存且响应码为 304(未修改),使用缓存
        if (cacheResponse != null) {
            if (networkResponse?.code == HTTP_NOT_MODIFIED) {
                val response = cacheResponse.newBuilder()
                    .headers(combine(cacheResponse.headers, networkResponse.headers))
                    .sentRequestAtMillis(networkResponse.sentRequestAtMillis)
                    .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
                    .cacheResponse(stripBody(cacheResponse))
                    .networkResponse(stripBody(networkResponse))
                    .build()

                networkResponse.body!!.close()

                cache!!.trackConditionalCacheHit()
                cache.update(cacheResponse, response)
                return response
            } else {
                cacheResponse.body?.closeQuietly()
            }
        }

        val response = networkResponse!!.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build()

        // 如果可缓存,写入缓存
        if (cache != null) {
            if (HttpHeaders.promisesBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
                val cacheRequest = cache.put(response)
                return cacheWritingResponse(cacheRequest, response)
            }

            if (HttpMethod.invalidatesCache(networkRequest.method)) {
                try {
                    cache.remove(networkRequest)
                } catch (_: IOException) {
                    // 忽略删除失败
                }
            }
        }

        return response
    }
}

缓存策略核心:

  • CacheStrategy 根据 Cache-Control、Expires、ETag、Last-Modified 计算是否可用缓存
  • 返回两个值:networkRequest(是否需要网络请求)和 cacheResponse(是否使用缓存)
  • 四种组合:
    • 都为 null → 504 错误
    • 仅 cacheResponse → 直接返回缓存
    • 仅 networkRequest → 发起网络请求并可能缓存响应
    • 都不为 null → 条件请求(带 If-None-Match 或 If-Modified-Since),如果返回 304 则使用缓存
4. ConnectInterceptor - 连接拦截器

建立与服务器的连接,复用连接池中的连接。

kotlin 复制代码
object ConnectInterceptor : Interceptor {
    
    override fun intercept(chain: Interceptor.Chain): Response {
        val realChain = chain as RealInterceptorChain
        val exchange = realChain.call.initExchange(chain)
        val connectedChain = realChain.copy(exchange = exchange)
        return connectedChain.proceed(realChain.request)
    }
}

看似简单,实际核心逻辑在 initExchange() 中,涉及连接池和连接复用。

5. CallServerInterceptor - 网络 I/O 拦截器

真正发送请求和接收响应的地方。

kotlin 复制代码
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
    
    override fun intercept(chain: Interceptor.Chain): Response {
        val realChain = chain as RealInterceptorChain
        val exchange = realChain.exchange!!
        val request = realChain.request
        val requestBody = request.body

        val sentRequestMillis = System.currentTimeMillis()

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

        var invokeStartEvent = true
        var responseBuilder: Response.Builder? = null
        
        if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
            // 发送请求体
            if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
                exchange.flushRequest()
                responseBuilder = exchange.readResponseHeaders(expectContinue = true)
                invokeStartEvent = false
            }
            
            if (responseBuilder == null) {
                if (requestBody.isDuplex()) {
                    // 双工模式(WebSocket)
                    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 {
            exchange.noRequestBody()
        }

        if (requestBody == null || !requestBody.isDuplex()) {
            exchange.finishRequest()
        }

        // 读取响应头
        if (responseBuilder == null) {
            responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
        }

        var response = responseBuilder
            .request(request)
            .handshake(exchange.connection.handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build()

        var code = response.code
        
        // 读取响应体
        response = if (forWebSocket && code == 101) {
            response.newBuilder()
                .body(EMPTY_RESPONSE)
                .build()
        } else {
            response.newBuilder()
                .body(exchange.openResponseBody(response))
                .build()
        }

        if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
            "close".equals(response.header("Connection"), ignoreCase = true)) {
            exchange.noNewExchangesOnConnection()
        }

        if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
            throw ProtocolException("...")
        }

        return response
    }
}

核心操作:

  • 通过 Exchange 写入请求头和请求体
  • 处理 100-continue 临时响应
  • 读取响应头和响应体
  • 支持双工模式(WebSocket)

连接池:TCP 连接的复用艺术

HTTP/1.1 引入了 Keep-Alive 机制,允许在一个 TCP 连接上发送多个请求。OkHttp 的连接池正是基于此实现连接复用。

ConnectionPool 核心结构

kotlin 复制代码
class ConnectionPool internal constructor(
    internal val delegate: RealConnectionPool
) {
    constructor(
        maxIdleConnections: Int = 5,
        keepAliveDuration: Long = 5,
        timeUnit: TimeUnit = TimeUnit.MINUTES
    ) : this(RealConnectionPool(
        taskRunner = TaskRunner.INSTANCE,
        maxIdleConnections = maxIdleConnections,
        keepAliveDuration = keepAliveDuration,
        timeUnit = timeUnit
    ))
}

class RealConnectionPool(
    private val maxIdleConnections: Int,
    keepAliveDuration: Long,
    timeUnit: TimeUnit
) {
    private val keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration)
    private val connections = ConcurrentLinkedQueue<RealConnection>()
    
    // 清理任务
    private val cleanupTask = object : Task("OkHttp ConnectionPool") {
        override fun runOnce(): Long {
            return cleanup(System.nanoTime())
        }
    }
}

核心参数:

  • maxIdleConnections:最大空闲连接数(默认 5)
  • keepAliveDuration:连接保活时长(默认 5 分钟)

连接复用逻辑

ExchangeFinder 查找连接:

kotlin 复制代码
private fun findConnection(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean
): RealConnection {
    var foundPooledConnection = false
    var result: RealConnection? = null

    // 1. 尝试复用当前 call 的连接
    val releasedConnection = call.connection
    if (releasedConnection != null && releasedConnection.noNewExchanges) {
        // 连接不可用,释放
        call.releaseConnectionNoEvents()
    }

    if (call.connection != null) {
        result = call.connection
        foundPooledConnection = true
    }

    if (result == null) {
        // 2. 从连接池查找可复用连接
        if (connectionPool.callAcquirePooledConnection(
                address, call, null, false)) {
            foundPooledConnection = true
            result = call.connection
        }
    }

    if (result == null) {
        // 3. 创建新连接
        result = RealConnection(connectionPool, route)
        call.connectionToCancel = result
        
        // 建立 TCP 和 TLS 连接
        result.connect(
            connectTimeout,
            readTimeout,
            writeTimeout,
            pingIntervalMillis,
            connectionRetryEnabled,
            call,
            eventListener
        )
        
        // 4. 再次尝试连接池(HTTP/2 多路复用)
        if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
            val toClose = result
            result = call.connection
            
            // 关闭刚创建的连接
            toClose.socket().closeQuietly()
        } else {
            // 将新连接放入连接池
            connectionPool.put(result)
        }
    }

    return result
}

连接复用判断:

kotlin 复制代码
internal fun transmitterAcquirePooledConnection(
    address: Address,
    transmitter: Transmitter,
    routes: List<Route>?,
    requireMultiplexed: Boolean
): Boolean {
    for (connection in connections) {
        if (requireMultiplexed && !connection.isMultiplexed) continue
        
        // 检查连接是否匹配
        if (!connection.isEligible(address, routes)) continue
        
        // 复用连接
        transmitter.acquireConnectionNoEvents(connection)
        return true
    }
    return false
}

连接是否可复用的条件:

kotlin 复制代码
internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
    // 1. 连接已达到最大并发流(HTTP/2)
    if (calls.size >= allocationLimit) return false
    
    // 2. 不接受新的 Exchange
    if (noNewExchanges) return false
    
    // 3. 主机名不匹配
    if (!this.route.address.equalsNonHost(address)) return false
    
    // 4. 主机名完全匹配
    if (address.url.host == this.route.address.url.host) {
        return true
    }
    
    // 5. HTTP/2 连接合并(Connection Coalescing)
    if (http2Connection == null) return false
    if (routes == null || !routeMatchesAny(routes)) return false
    if (address.hostnameVerifier !== OkHostnameVerifier) return false
    if (!supportsUrl(address.url)) return false
    
    // 证书检查
    try {
        address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates)
    } catch (_: SSLPeerUnverifiedException) {
        return false
    }
    
    return true
}

连接清理机制

kotlin 复制代码
fun cleanup(now: Long): Long {
    var inUseConnectionCount = 0
    var idleConnectionCount = 0
    var longestIdleConnection: RealConnection? = null
    var longestIdleDurationNs = Long.MIN_VALUE

    // 遍历所有连接,找出最长空闲连接
    for (connection in connections) {
        synchronized(connection) {
            if (pruneAndGetAllocationCount(connection, now) > 0) {
                inUseConnectionCount++
            } else {
                idleConnectionCount++
                val idleDurationNs = now - connection.idleAtNs
                if (idleDurationNs > longestIdleDurationNs) {
                    longestIdleDurationNs = idleDurationNs
                    longestIdleConnection = connection
                }
            }
        }
    }

    when {
        // 超过保活时长或超过最大空闲连接数:清理
        longestIdleDurationNs >= keepAliveDurationNs ||
        idleConnectionCount > maxIdleConnections -> {
            connections.remove(longestIdleConnection)
            longestIdleConnection?.socket()?.closeQuietly()
            return 0L // 立即再次清理
        }
        
        idleConnectionCount > 0 -> {
            // 返回下次清理时间
            return keepAliveDurationNs - longestIdleDurationNs
        }
        
        inUseConnectionCount > 0 -> {
            // 所有连接都在使用,保活时长后再检查
            return keepAliveDurationNs
        }
        
        else -> {
            // 没有连接,停止清理任务
            return -1
        }
    }
}

清理策略:

  • 找出最长空闲连接
  • 如果空闲时间超过 keepAliveDuration 或空闲连接数超过 maxIdleConnections,移除该连接
  • 返回下次清理的延迟时间

缓存策略:HTTP 规范的忠实实现

OkHttp 的缓存完全遵循 HTTP/1.1 缓存规范(RFC 7234)。

Cache 存储结构

kotlin 复制代码
class Cache internal constructor(
    internal val internalCache: InternalCache,
    directory: File,
    maxSize: Long
) : Closeable, Flushable {
    
    internal val cache = DiskLruCache(
        fileSystem = FileSystem.SYSTEM,
        directory = directory,
        appVersion = VERSION,
        valueCount = ENTRY_COUNT,
        maxSize = maxSize,
        taskRunner = TaskRunner.INSTANCE
    )
}

使用 DiskLruCache 实现磁盘缓存,每个缓存条目包含:

  • Entry 0:响应头
  • Entry 1:响应体

CacheStrategy 计算缓存策略

kotlin 复制代码
class CacheStrategy internal constructor(
    val networkRequest: Request?,
    val cacheResponse: Response?
) {
    
    class Factory(
        private val nowMillis: Long,
        private val request: Request,
        private val cacheResponse: Response?
    ) {
        fun compute(): CacheStrategy {
            val candidate = computeCandidate()
            
            // 如果请求要求使用缓存,但缓存不满足条件,返回 504
            if (candidate.networkRequest != null && 
                request.cacheControl.onlyIfCached) {
                return CacheStrategy(null, null)
            }
            
            return candidate
        }
        
        private fun computeCandidate(): CacheStrategy {
            // 1. 没有缓存,必须发起网络请求
            if (cacheResponse == null) {
                return CacheStrategy(request, null)
            }
            
            // 2. HTTPS 但缺少握手信息,不使用缓存
            if (request.isHttps && cacheResponse.handshake == null) {
                return CacheStrategy(request, null)
            }
            
            // 3. 根据响应码和头信息判断是否可缓存
            if (!isCacheable(cacheResponse, request)) {
                return CacheStrategy(request, null)
            }
            
            val requestCaching = request.cacheControl
            
            // 4. 请求不允许使用缓存
            if (requestCaching.noCache || hasConditions(request)) {
                return CacheStrategy(request, null)
            }
            
            val responseCaching = cacheResponse.cacheControl
            val ageMillis = cacheResponseAge()
            var freshMillis = computeFreshnessLifetime()
            
            if (requestCaching.maxAgeSeconds != -1) {
                freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))
            }
            
            var minFreshMillis: Long = 0
            if (requestCaching.minFreshSeconds != -1) {
                minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())
            }
            
            var maxStaleMillis: Long = 0
            if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {
                maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())
            }
            
            // 5. 缓存仍然新鲜,直接使用
            if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
                val builder = cacheResponse.newBuilder()
                if (ageMillis + minFreshMillis >= freshMillis) {
                    builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"")
                }
                val oneDayMillis = 24 * 60 * 60 * 1000L
                if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
                    builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"")
                }
                return CacheStrategy(null, builder.build())
            }
            
            // 6. 缓存过期,构建条件请求
            val conditionName: String
            val conditionValue: String?
            when {
                etag != null -> {
                    conditionName = "If-None-Match"
                    conditionValue = etag
                }
                lastModified != null -> {
                    conditionName = "If-Modified-Since"
                    conditionValue = lastModifiedString
                }
                servedDate != null -> {
                    conditionName = "If-Modified-Since"
                    conditionValue = servedDateString
                }
                else -> {
                    // 没有条件信息,重新请求
                    return CacheStrategy(request, null)
                }
            }
            
            val conditionalRequest = request.newBuilder()
                .header(conditionName, conditionValue!!)
                .build()
            
            return CacheStrategy(conditionalRequest, cacheResponse)
        }
        
        private fun computeFreshnessLifetime(): Long {
            val responseCaching = cacheResponse!!.cacheControl
            
            // 优先使用 max-age
            if (responseCaching.maxAgeSeconds != -1) {
                return SECONDS.toMillis(responseCaching.maxAgeSeconds.toLong())
            }
            
            // 其次使用 Expires
            val expires = this.expires
            if (expires != null) {
                val servedMillis = servedDate?.time ?: receivedResponseMillis
                val delta = expires.time - servedMillis
                return if (delta > 0L) delta else 0L
            }
            
            // 启发式缓存:(当前时间 - Last-Modified) * 10%
            if (lastModified != null && cacheResponse.request.url.query == null) {
                val servedMillis = servedDate?.time ?: sentRequestMillis
                val delta = servedMillis - lastModified.time
                return if (delta > 0L) delta / 10 else 0L
            }
            
            return 0L
        }
    }
}

缓存策略决策树:

sql 复制代码
有缓存吗?
  ├─ 否 → 网络请求
  └─ 是 → HTTPS 且无握手信息?
      ├─ 是 → 网络请求
      └─ 否 → 响应可缓存?
          ├─ 否 → 网络请求
          └─ 是 → 请求禁止缓存?
              ├─ 是 → 网络请求
              └─ 否 → 缓存新鲜?
                  ├─ 是 → 使用缓存
                  └─ 否 → 有 ETag/Last-Modified?
                      ├─ 是 → 条件请求(If-None-Match/If-Modified-Since)
                      └─ 否 → 网络请求

自定义拦截器实战

日志拦截器

kotlin 复制代码
class LoggingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        
        val startTime = System.nanoTime()
        Log.d("OkHttp", "发送请求: ${request.url}")
        
        val response = chain.proceed(request)
        
        val duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
        Log.d("OkHttp", "收到响应: ${response.code} 耗时: ${duration}ms")
        
        return response
    }
}

val client = OkHttpClient.Builder()
    .addInterceptor(LoggingInterceptor())
    .build()

Token 自动刷新拦截器

kotlin 复制代码
class TokenAuthenticator(private val tokenManager: TokenManager) : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        // 避免无限重试
        if (response.request.header("Authorization") != null && 
            responseCount(response) >= 3) {
            return null
        }
        
        synchronized(this) {
            val newToken = tokenManager.refreshToken()
            
            return response.request.newBuilder()
                .header("Authorization", "Bearer $newToken")
                .build()
        }
    }
    
    private fun responseCount(response: Response): Int {
        var count = 1
        var priorResponse = response.priorResponse
        while (priorResponse != null) {
            count++
            priorResponse = priorResponse.priorResponse
        }
        return count
    }
}

val client = OkHttpClient.Builder()
    .authenticator(TokenAuthenticator(tokenManager))
    .build()

网络缓存拦截器

kotlin 复制代码
class CacheControlInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        
        // 无网络时强制使用缓存
        if (!isNetworkAvailable()) {
            request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build()
        }
        
        val response = chain.proceed(request)
        
        // 有网络时设置缓存有效期
        if (isNetworkAvailable()) {
            val maxAge = 60 // 缓存 60 秒
            return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, max-age=$maxAge")
                .build()
        } else {
            // 无网络时缓存 7 天
            val maxStale = 60 * 60 * 24 * 7
            return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
                .build()
        }
    }
    
    private fun isNetworkAvailable(): Boolean {
        // 检查网络连接
        return true
    }
}

小结

OkHttp 的源码设计体现了多个优秀的设计模式和工程实践:

责任链模式(拦截器链):

  • 将复杂流程分解为独立步骤
  • 每个拦截器职责单一
  • 易于扩展和自定义

连接池复用:

  • TCP 连接复用减少握手开销
  • HTTP/2 多路复用提升并发能力
  • 智能清理机制平衡性能与资源

缓存策略:

  • 完全遵循 HTTP 规范
  • 支持条件请求和协商缓存
  • 减少不必要的网络请求

通过深入理解 OkHttp 源码,我们不仅能更好地使用它,还能在遇到问题时快速定位根因,甚至基于它的设计思想构建自己的框架。这就是阅读优秀开源项目源码的价值。

相关推荐
蒲公英内测分发1 小时前
智能手环出海测试,海外合作方分散在不同国家,Android 测试包怎么持续分发和更新?
android·前端·人工智能·测试工具·智能手表
朱涛的自习室1 小时前
高价软件已死,我让 AI 开启了「平替计划」(Day 7 / 7)
android·前端·github
脚踏实地,坚持不懈!1 小时前
Android 图形系统:图形总览 + SurfaceFlinger 与 WindowManager
android
2501_915918411 小时前
iOS 上架注意事项有哪些?Appuploader 的四个阶段自查清单
android·ios·小程序·https·uni-app·iphone·webview
张小姐的猫2 小时前
【MySQL】表的约束
android·linux·服务器·数据库·c++·mysql·adb
Kapaseker2 小时前
为什么程序员始终无法彻底解决内存泄漏?
android·kotlin
汤姆宝库2 小时前
推理工程深拆:调度、量化与投机解码
android·开发语言·kotlin
AirDroid_cn2 小时前
隔离风险:用备用机测试不明APP,电脑远程操控安卓保主力机安全
android·安全