OkHttp 源码解析:拦截器链、连接池与缓存策略的设计精髓
OkHttp 是 Android 和 Java 生态中最流行的 HTTP 客户端库,以其高效、简洁的 API 和强大的功能著称。无论是直接使用还是通过 Retrofit 间接调用,OkHttp 都在幕后默默支撑着网络请求的高效执行。
深入理解 OkHttp 的源码,不仅能帮助我们更好地使用它,还能学习到优秀框架的设计思想:责任链模式、连接复用、缓存策略等核心机制。
OkHttp 架构概览
OkHttp 的核心设计围绕三大支柱:
- 拦截器链(Interceptor Chain):责任链模式,将请求处理分解为多个独立步骤
- 连接池(ConnectionPool):复用 TCP 连接,减少握手开销
- 缓存策略(Cache):遵循 HTTP 缓存规范,减少不必要的网络请求
一个请求的生命周期
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 核心代码简化:
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)。
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 请求头。
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 缓存规范,决定是否使用缓存、是否发起网络请求。
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 - 连接拦截器
建立与服务器的连接,复用连接池中的连接。
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 拦截器
真正发送请求和接收响应的地方。
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 核心结构
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 查找连接:
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
}
连接复用判断:
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
}
连接是否可复用的条件:
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
}
连接清理机制
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 存储结构
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 计算缓存策略
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
}
}
}
缓存策略决策树:
有缓存吗?
├─ 否 → 网络请求
└─ 是 → HTTPS 且无握手信息?
├─ 是 → 网络请求
└─ 否 → 响应可缓存?
├─ 否 → 网络请求
└─ 是 → 请求禁止缓存?
├─ 是 → 网络请求
└─ 否 → 缓存新鲜?
├─ 是 → 使用缓存
└─ 否 → 有 ETag/Last-Modified?
├─ 是 → 条件请求(If-None-Match/If-Modified-Since)
└─ 否 → 网络请求
自定义拦截器实战
日志拦截器
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 自动刷新拦截器
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()
网络缓存拦截器
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 源码,我们不仅能更好地使用它,还能在遇到问题时快速定位根因,甚至基于它的设计思想构建自己的框架。这就是阅读优秀开源项目源码的价值。