OkHttp源码分析

OkHttp 源码分析

OkHttp

网络底层库,基于 HTTP 协议封装的一套请求客户端。


OkHttp 特性

  • 使用连接池来复用连接以提高效率
  • 默认使用 gzip 传输,降低了传输内容的大小
  • 提供了对 HTTP 响应的缓存机制,可避免不必要的网络请求
  • 网络出现问题时,会自动重试一个主机的多个 IP
  • 提供对 HTTP2 协议的支持,HTTP2 协议是通过一个主机发出的所有的请求可以共享相同的套接字连接

基本使用

导入库
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

创建 OkHttp 实例
OkHttpClient client = new OkHttpClient
        .Builder()
        .build();

创建 Request
Request request = new Request
        .Builder()
        .url("https://api.github.com/users/octocat/repos")
        .build();

构建 Call 对象并发起请求
Call call = client.newCall(request);
// enqueue:异步,execute:同步
call.enqueue(new Callback() {
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {

    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        Log.d("TAG", "response: " + response.body().string());
    }
});

源码分析

OkHttpClient

OkHttpClient 是 OkHttp 的配置类,通过构建者模式生成。

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
  ...
    
  class Builder constructor() {
    internal var dispatcher: Dispatcher = Dispatcher()// 调度器,用于调度后台发起的网络请求,有后台总请求数(64)和单主机总计数(5)的控制
    internal var connectionPool: ConnectionPool = ConnectionPool()// 连接池,复用连接
    internal val interceptors: MutableList = mutableListOf()// 拦截器,大多数的拦截器都应该配置到这里
    internal val networkInterceptors: MutableList = mutableListOf()// 网络拦截器,直接和网络请求交互的拦截器配置到这里,如查看返回的301报文,或未解压的Response Body
    internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()// 流程监听器
    internal var retryOnConnectionFailure = true// 在请求失败的时候是否自动重试。注意 大多数的请求失败并不属于 OkHttp 所定义的 需要重试,这种情况只适用于 同一个域名的多个 IP 切换重试、Socket 失效重试 等情况
    internal var authenticator: Authenticator = Authenticator.NONE// 用于自动重新认证,配置之后,在请求收到 401 状态码的响应时,会直接调用 authenticator,手动加入 Authorization header 之后自动重新发起请求
    internal var followRedirects = true// 是否支持重定向
    internal var followSslRedirects = true// 是否支持从 http到https 或https到http 的重定向
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES// 管理 Cookie 的控制器,OkHttp 提供了 Cookie 存取的判断支持(即什么时候需要存 Coookie,什么时候需要读取Cookie,但没有给出具体的实现),如果需要存取 Cookie ,需要自己实现,如保存在内存或本地缓存
    internal var cache: Cache? = null// 缓存设置,默认没有,如果需要用,需要自己配置出 Cache 存储的文件位置以及存储空间上限
    internal var dns: Dns = Dns.SYSTEM/// DNS
    internal var proxy: Proxy? = null// 代理
    internal var proxySelector: ProxySelector? = null// 代理选择器
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE// 代理服务器认证
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()// socket配置
    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null// https scoket配置
    internal var x509TrustManagerOrNull: X509TrustManager? = null// 证书
    internal var connectionSpecs: List = DEFAULT_CONNECTION_SPECS// 应用层支持的 Socket 设置,即使用明文传输(用于HTTP),还是某个版本的 TLS (用于 HTTPS)
    internal var protocols: List = DEFAULT_PROTOCOLS// 支持应用层协议,即 HTTP/1.1 HTTP/2 等
    internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier// 域名校验,用于验证 HTTPS 握手过程中,下载到的证书所属者是否和自己要访问的主机名一致
    internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT// 用于设置 HTTPS 握手过程中针对某个 Host 额外的 Certificate Public Key Pinner,即把网站证书链中的某一个证书公钥直接拿来提前配置到 OkHttpClient 中,做为正常的证书验证机制之外的一次额外验证
    internal var certificateChainCleaner: CertificateChainCleaner? = null
    internal var callTimeout = 0// 请求超时
    internal var connectTimeout = 10_000// 建立 TCP 或 TLS 连接的超时时间
    internal var readTimeout = 10_000// 发起请求到读到响应数据的超时时间
    internal var writeTimeout = 10_000// 写入超时(发起请求并被目标服务器接收的超时时间)
    internal var pingInterval = 0// 
    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
    internal var routeDatabase: RouteDatabase? = null
     
    ...
  }
  ...
}

Request

Reqeust 也采用了建造者模式

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map, Any>
  
  ...
  
  open class Builder {
    internal var url: HttpUrl? = null// 请求的url
    internal var method: String// 请求的方法 GET/POST
    internal var headers: Headers.Builder// 请求头
    internal var body: RequestBody? = null// 请求内容
    
    ...
    open fun build(): Request {
      return Request(
          checkNotNull(url) { "url == null" },
          method,
          headers.build(),
          body,
          tags.toImmutableMap()
      )
    }
  }
  ...
  
)

Call

client.newCall() 方法实际会返回一个 RealCall 对象,它是 Call 接口的实现。当调用 RealCall .execute() 的方法时,RealCall.getResponseWithInterceptorChain() 会被调用,它会发起网络并拿到返回的响应,装进一个 Response 对象并作为返回值返回;

RealCall.enqueue() 调用的时候,会使用 Dispatcher 的线程池来把请求放在后台线程进行,实际上调用的也是RealCall.getResponseWithInterceptorChain().

// RealCall类
override fun enqueue(responseCallback: Callback) {
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  callStart()
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

// AsyncCall类
internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
		...
    fun executeOn(executorService: ExecutorService) {
      ...
      executorService.execute(this)// 利用线程池执行自身,会调用run方法
      ...
    }

    override fun run() {
     	...
      val response = getResponseWithInterceptorChain()
      responseCallback.onResponse(this@RealCall, response)
     	...   
    }
  }

// Dispatcher类
internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)
      if (!call.call.forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute()// 直接看这行
  }

// Dispatcher类
private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()

        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        executableCalls.add(asyncCall)
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)// 调用线程池执行
    }

    return isRunning
  }

执行拦截器

getResponseWithInterceptorChain() 方法所做的事,把配置好的 Interceptor 放在一个 List 中,然后作为一个参数,创建一个 RealInterceptorChain 对象,并调用 chain.proceed(request) 来发起请求和获取响应。

在 RealInterceptorChain 中,每个 interceptor 会依次调用自己的 intercept() 方法。这个方法会做三件事:

  1. 对请求进行预处理
  2. 预处理之后,重新调用 RealIntercepterChain.procced() 把请求交给下一个 Interceptor
  3. 在下一个 Interceptor 处理完成并返回之后,拿到 Response 进行过后续的处理

// RealCall类
internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf()
  interceptors += client.interceptors// 开发者自定义的拦截,进行最早的预处理工作,以及在收到 Response 做善后的处理,比如 统一的header 或 网络请求日志打印 可以在这里进行配置
  interceptors += RetryAndFollowUpInterceptor(client)// 它会对连接做一些初始化工作,并且负责在请求失败时重试,以及重定向的自动后续请求。它的存在,可以让重试和重定向对于开发者是无感知的
  interceptors += BridgeInterceptor(client.cookieJar)// 对 HTTP 做一些额外处理,如Content-Length 的计算和添加,gzip的支持等
  interceptors += CacheInterceptor(client.cache)// 负责 Cache 的处理,如果本地有了可用的Cache,一个请求可以在没有发生实质网络交互的请求下就返回缓存结果
  interceptors += ConnectInterceptor// 负责建立连接,ConnectInterceptor会创建出网络请求所需要的 TCP连接 或 TLS连接,并且会创建出对应的 HttpCodec 对象,用于编码解码 HTTP 请求
  if (!forWebSocket) {
    interceptors += client.networkInterceptors// 一般用于网络调试
  }
  interceptors += CallServerInterceptor(forWebSocket)// 实质的的请求与响应的 I/O 操作,即往 Socket 里写入请求数据,和从 Socket 里读取响应数据 

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

RetryAndFollowUpInterceptor

负责重试和请求重定向的一个拦截器

class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
  
  companion object {
    // 最大重试次数或重定向次数
    private const val MAX_FOLLOW_UPS = 20
  }

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    ...
    while (true) {
      call.enterNetworkInterceptorExchange(request, newExchangeFinder)

      var response: Response
      var closeActiveExchange = true
      try {
        if (call.isCanceled()) {
          throw IOException("Canceled")
        }

        try {
          response = realChain.proceed(request)// 让下一个拦截器处理
          newExchangeFinder = true
        } catch (e: RouteException) {
          if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
            throw e.firstConnectException.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e.firstConnectException
          }
          newExchangeFinder = false
          continue// 继续重试
        } catch (e: IOException) {
          if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
            throw e.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e
          }
          newExchangeFinder = false
          continue// 继续重试
        }

        ...
        val followUp = followUpRequest(response, exchange)// 判断是否重定向

        if (followUp == null) {
          if (exchange != null && exchange.isDuplex) {
            call.timeoutEarlyExit()
          }
          closeActiveExchange = false
          return response
        }

        val followUpBody = followUp.body
        if (followUpBody != null && followUpBody.isOneShot()) {
          closeActiveExchange = false
          return response
        }

        response.body?.closeQuietly()

        if (++followUpCount > MAX_FOLLOW_UPS) {
          throw ProtocolException("Too many follow-up requests: $followUpCount")
        }

        request = followUp
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
  }

}

BridgeInterceptor

桥接,对请求和返回的数据进行转换。

class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

  @Throws(IOException::class)
  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")
    }

    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())// 调用下一个拦截器

    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)

    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      val responseBody = networkResponse.body
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        val strippedHeaders = networkResponse.headers.newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()
        responseBuilder.headers(strippedHeaders)
        val contentType = networkResponse.header("Content-Type")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }

    return responseBuilder.build()
  }

}

CacheInterceptor

缓存数据,节省流量,提高响应数据。

class CacheInterceptor(internal val cache: Cache?) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    ...
    // 读取候选缓存
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

		...
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }

		...
    var networkResponse: Response? = null
    try {
      networkResponse = chain.proceed(networkRequest)// 执行下一个拦截器
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      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()

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache!!.trackConditionalCacheHit()
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

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

    if (cache != null) {// 对数据进行缓存
     	...
    }

    return response
  }

}

ConnectInterceptor

建立连接的拦截器

object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)// 开始连接工作
    val connectedChain = realChain.copy(exchange = exchange)// 将exchange传递到下一个拦截器中,exchange用来发送http和接收响应
    return connectedChain.proceed(realChain.request)// 进行下一个拦截器工作
  }
}

CallServerInterceptor

这是链中最后一个拦截器,它负责对服务器进行网络调用

class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  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)
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
      if (responseBuilder == null) {
        if (requestBody.isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
        }
      } else {
        exchange.noRequestBody()
        if (!exchange.connection.isMultiplexed) {
          
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      exchange.noRequestBody()
    }

    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()
    }
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    var code = response.code
    if (code == 100) {
      // Server sent a 100-continue even though we did not request one. Try again to read the actual
      // response status.
      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
    }

    exchange.responseHeadersEnd(response)

    response = if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      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(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }
    return response
  }
}

你可能感兴趣的:(OkHttp)