okhttp 请求过程源码分析

本篇基于 okhttp 4.9.3 进行分析学习。

okhttp 项目地址

前言

Android 项目开发中离不开网络请求,说到网络请求一定会想到 okhttp。

okhttp 是如何完成一次请求的呢,接下来我们一步步来分析。

首先看下完整的流程图,此处直接使用 Piasy 大佬的图

image.png

一、如何发起请求

1. Android 工程中使用配置

添加网络请求权限 <uses-permission android:name="android.permission.INTERNET"/>

添加依赖 implementation "com.squareup.okhttp3:okhttp:4.9.3"

2. 发起请求

以发送 GET 请求为例,其他类型请求也是通过在创建 requst 对象时进行设置。

// 创建 OkHttpClient 实例对象,设置了超时时间
val client = OkHttpClient.Builder()
    .callTimeout(10, TimeUnit.SECONDS)
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(10, TimeUnit.SECONDS)
    .writeTimeout(10, TimeUnit.SECONDS)
    .build()

// 创建 requset 对象,设置 url、请求方式、header 等信息
val request = Request.Builder().url(url).build()

// 同步执行,需放到子线程中执行
GlobalScope.launch(Dispatchers.IO) {
    val response = client.newCall(request).execute().body?.string()
}

// 异步执行
client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        Log.d(TAG, e.message ?: "have IOException")
    }

    override fun onResponse(call: Call, response: Response) {
        val response = response.body?.string()
        Log.d(TAG, "enqueue() response = $response")
    }
})

复制代码

二、发起一次请求都经历了什么

分析上面 GET 请求时用到的对象,源码只保留流程中重要的部分。

1. OkHttpClient

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

    ...
    
    // OkHttpClient 可以构建 Call 的原因,因为实现了 Call.Factory
    override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

    ...

    // 默认构建参数
    class Builder constructor() {
      internal var dispatcher: Dispatcher = Dispatcher() // 调度器
      internal var connectionPool: ConnectionPool = ConnectionPool() // 连接池
      internal val interceptors: MutableList<Interceptor> = mutableListOf() // 拦截器
      internal val networkInterceptors: MutableList<Interceptor> = mutableListOf() // 网络拦截器 
      internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
      internal var retryOnConnectionFailure = true // 连接失败时重试
      internal var authenticator: Authenticator = Authenticator.NONE // 验证器
      internal var followRedirects = true // 允许重定向
      internal var followSslRedirects = true // 允许 https 同 http 之间的重定向
      internal var cookieJar: CookieJar = CookieJar.NO_COOKIES // cookie
      internal var cache: Cache? = null // 缓存
      internal var dns: Dns = Dns.SYSTEM
      internal var proxy: Proxy? = null
      internal var proxySelector: ProxySelector? = null
      internal var proxyAuthenticator: Authenticator = Authenticator.NONE
      internal var socketFactory: SocketFactory = SocketFactory.getDefault()
      internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
      internal var x509TrustManagerOrNull: X509TrustManager? = null
      internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
      internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
      internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
      internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
      internal var certificateChainCleaner: CertificateChainCleaner? = null
      internal var callTimeout = 0
      internal var connectTimeout = 10_000
      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
      
      // 传入自己构建的参数
      internal constructor(okHttpClient: OkHttpClient) : this() {
        this.dispatcher = okHttpClient.dispatcher
        this.connectionPool = okHttpClient.connectionPool
        this.interceptors += okHttpClient.interceptors
        this.networkInterceptors += okHttpClient.networkInterceptors
        this.eventListenerFactory = okHttpClient.eventListenerFactory
        this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
        this.authenticator = okHttpClient.authenticator
        this.followRedirects = okHttpClient.followRedirects
        this.followSslRedirects = okHttpClient.followSslRedirects
        this.cookieJar = okHttpClient.cookieJar
        this.cache = okHttpClient.cache
        this.dns = okHttpClient.dns
        this.proxy = okHttpClient.proxy
        this.proxySelector = okHttpClient.proxySelector
        this.proxyAuthenticator = okHttpClient.proxyAuthenticator
        this.socketFactory = okHttpClient.socketFactory
        this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
        this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
        this.connectionSpecs = okHttpClient.connectionSpecs
        this.protocols = okHttpClient.protocols
        this.hostnameVerifier = okHttpClient.hostnameVerifier
        this.certificatePinner = okHttpClient.certificatePinner
        this.certificateChainCleaner = okHttpClient.certificateChainCleaner
        this.callTimeout = okHttpClient.callTimeoutMillis
        this.connectTimeout = okHttpClient.connectTimeoutMillis
        this.readTimeout = okHttpClient.readTimeoutMillis
        this.writeTimeout = okHttpClient.writeTimeoutMillis
        this.pingInterval = okHttpClient.pingIntervalMillis
        this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
        this.routeDatabase = okHttpClient.routeDatabase
      }
      
      ...
}
复制代码

使用建造者模式,用来设置拦截器、超时时间、缓存、重定向等属性。

2. Request

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<Class<*>, Any>
) {
    
    ...
    
    
    open class Builder {
      internal var url: HttpUrl? = null
      internal var method: String
      internal var headers: Headers.Builder
      internal var body: RequestBody? = null

      internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()

      constructor() {
        this.method = "GET"
        this.headers = Headers.Builder()
      }

      internal constructor(request: Request) {
        this.url = request.url
        this.method = request.method
        this.body = request.body
        this.tags = if (request.tags.isEmpty()) {
          mutableMapOf()
        } else {
          request.tags.toMutableMap()
        }
        this.headers = request.headers.newBuilder()
      }

      open fun url(url: HttpUrl): Builder = apply {
        this.url = url
      }
      
      ...
}
复制代码

也是使用建造者模式,来设置请求方法、url、请求体、header 信息。

3. Call

interface Call : Cloneable {
  // 返回 call 的 request 对象
  fun request(): Request

  // 执行同步请求
  @Throws(IOException::class)
  fun execute(): Response

  // 执行异步请求
  fun enqueue(responseCallback: Callback)
    
  // 取消
  fun cancel()
  
  // 是否已经执行请求
  fun isExecuted(): Boolean

  // 是否已经取消
  fun isCanceled(): Boolean
  
  // 超时信息
  fun timeout(): Timeout

  // 克隆当前 call,返回新的对象方便再次执行请求,同一个 call 只能执行一次
  public override fun clone(): Call

  fun interface Factory {
    fun newCall(request: Request): Call
  }
}
复制代码

接口类,定义了在一次请求中可能用到的方法。

4. RealCall

实现 Call 接口,是最终的请求执行者,下面按同步请求和异步请求分成两部分来说。

a、同步请求部分
// RealCall.execute
override fun execute(): Response {
  // 每个 call 只能请求一次
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  timeout.enter()
  callStart()
  try {
    // 将当前 call 添加到 Dispatcher 类的 runningSyncCalls 列表中
    client.dispatcher.executed(this)
    // 经过一系列拦截器拿到请求结果
    return getResponseWithInterceptorChain()
  } finally {
    // 将当前 call 从 Dispatcher 类的 runningSyncCalls 列表中移除
    client.dispatcher.finished(this)
  }
}
复制代码
// RealCall.getResponseWithInterceptorChain
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf<Interceptor>()
  // 开始添加拦截器
  // 在 OkHttpClient 中设置的 拦截器
  interceptors += client.interceptors
  // 处理重试和重定向的拦截器
  interceptors += RetryAndFollowUpInterceptor(client)
  // 将应用请求转换为网络请求,将请求结果转换为用户友好响应的拦截器
  interceptors += BridgeInterceptor(client.cookieJar)
  // 读取缓存、更新缓存的拦截器
  interceptors += CacheInterceptor(client.cache)
  // 和服务器连接的拦截器
  interceptors += ConnectInterceptor
  if (!forWebSocket) {
    // 在 OkHttpClient 中设置的 networkInterceptors 
    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() 该方法中,dispatcher 用来改变执行状态。

b、异步请求部分
// RealCall.enqueue
override fun enqueue(responseCallback: Callback) {
  // 每个 call 只能请求一次
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  callStart()
  // 将 AsyncCall 添加到 Dispatcher.readyAsyncCalls 中
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}
复制代码

接下来看 enqueue() 方法

// Dispatcher.enqueue
internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
    // 添加到 readyAsyncCalls 中
    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()
}
复制代码

接下来看 promoteAndExecute() 方法

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

  val executableCalls = mutableListOf<AsyncCall>()
  val isRunning: Boolean
  synchronized(this) {
    val i = readyAsyncCalls.iterator()
    // 遍历 readyAsyncCalls
    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 中
      executableCalls.add(asyncCall)
      runningAsyncCalls.add(asyncCall)
    }
    isRunning = runningCallsCount() > 0
  }
  // 遍历 executableCalls 开始执行
  for (i in 0 until executableCalls.size) {
    val asyncCall = executableCalls[i]
    asyncCall.executeOn(executorService)
  }

  return isRunning
}
复制代码

执行的 run 方法

// RealCall#AsyncCall.run
override fun run() {
  threadName("OkHttp ${redactedUrl()}") {
    var signalledCallback = false
    timeout.enter()
    try {
      // 同步请求时分析过的方法
      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)
    }
  }
}
复制代码

看到这里再去看开头的流程图就很容易了

分析了请求的大致流程,细节分析后续有待完善。

参考

拆轮子系列:拆 OkHttp

AtomicBoolean介绍与使用

おすすめ

転載: juejin.im/post/7075967468778291231