「okhttp3 4.9.3 版本简单解析」

「okhttp3 4.9.3 版本简单解析」
一、写在前面

关于okhttp3的解析网上已经有非常多优秀的博文了,每每看完都觉得醍醐灌顶,豁然开朗。但等不了几天再回头看,还是跟当初一样陌生,究其根本原因,我们不过是在享受着别人的成果跟着别人的思路云阅读源码了一遍。okhttp从早期的Java版本到Kotlin版本一直不断优化升级,实现细节上也作出了调整。重读源码加上自身的思考能深刻的理解okhttp的实现原理。

二、从基本使用说起
  • 项目中引入依赖,Github地址okhttp
dependencies {
  //...
  implementation("com.squareup.okhttp3:okhttp:4.9.3")
}
  • 从一个简单的同步(execute)请求入手分析整体的加载流程:
private fun clientNetWork() {
     val request: Request = Request.Builder()
         .url("https://www.baidu.com")
         .build()
     OkHttpClient().newCall(request).execute()
}

execute()开始,发现其实是一个接口中的方法(Call),这个很好理解根据官方的解释,Call其实是一个待执行的请求,并且这个请求所要的参数已经被准备好;当然既然是请求,那么它是可以被取消的。其次代表单个请求与响应流,因此不能够被再次执行。

A call is a request that has been prepared for execution. A call can be canceled. As this object represents a single request/response pair (stream), it cannot be executed twice.

Call接口具体的代码实现,重点关注同步执行方法execute()与异步请求enqueue()

interface Call : Cloneable {
  fun request(): Request
  //同步请求
  @Throws(IOException::class)
  fun execute(): Response
  //异步请求
  fun enqueue(responseCallback: Callback)
  
  fun cancel()
  
  fun isExecuted(): Boolean
  
  fun isCanceled(): Boolean
  
  fun interface Factory {
   fun newCall(request: Request): Call
  }
}

Call作为接口,那么具体的实现细节则需要看它的实现类,RealCall作为Call的实现类,先找到对execute()的重写。

  • RealCall-是应用程序与网络之间的桥梁,看看具体有什么作用,首先分析同步请求execute()方法的实现:
override fun execute(): Response {
  check(executed.compareAndSet(false, true)) { "Already Executed" } //#1
  timeout.enter()//#2
  callStart()//#3
  try {
    client.dispatcher.executed(this)//#4
    return getResponseWithInterceptorChain()//#5
  } finally {
    client.dispatcher.finished(this)//#6
  }
}

代码很少,逐步分析,首先对同步请求进行了检查-判断请求是否已经被执行过了;而这里使用的是并发包下的原子类CAS乐观锁,这里使用CAS比较算法目的也是为提升效率。其次是超时时间的判断,这个比较简单。在看callStart()的具体实现。上代码:

  private fun callStart() {
    this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
    eventListener.callStart(this)
  }

看名称猜测应该是事件监听之类的,可能是包括一些信息的记录与打印。回到RealCall类中,看看这个eventListener的作用到底是什么:

internal val eventListener: EventListener = client.eventListenerFactory.create(this)

继续向下可以知道这个EventListener是一个抽象类,而项目中其唯一实现类为LoggingEventListener,猜测还是有依据的,继续往下看:

abstract class EventListener {
    open fun callStart(
    call: Call
  ) {
  }
  //省略了其他的方法
}

实现类LoggingEventListener中对此方法的具体实现:

class LoggingEventListener private constructor(
  private val logger: HttpLoggingInterceptor.Logger
) : EventListener() {
    override fun callStart(call: Call) {
    startNs = System.nanoTime()
    logWithTime("callStart: ${call.request()}")
  }
}

总结这个callStart()的作用,当同步请求或者异步请求被加到队列时,callStart()会被立即执行(在没有达到线程限制的情况下)记录请求开始的时间与请求的一些信息。如下:

  override fun callStart(call: Call) {
    startNs = System.nanoTime()
    logWithTime("callStart: ${call.request()}")
  }
  
 	private fun logWithTime(message: String) {
    val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
    logger.log("[$timeMs ms] $message")
  }

代卖第四段#4 client.dispatcher.executed(this),看样子是在这里开启执行的,可实际真是如此嘛?回到OkHttpClient类中,看看这个分发器dispatcher到底是什么。

@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

具体实现类Dispatcher代码(保留重要代码):

class Dispatcher constructor() {
  /** Ready async calls in the order they'll be run. */
  private val readyAsyncCalls = ArrayDeque<AsyncCall>()
  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private val runningSyncCalls = ArrayDeque<RealCall>()

  constructor(executorService: ExecutorService) : this() {
    this.executorServiceOrNull = executorService
  }
    @get:Synchronized var maxRequests = 64
    set(maxRequests) {
      require(maxRequests >= 1) { "max < 1: $maxRequests" }
      synchronized(this) {
        field = maxRequests
      }
      promoteAndExecute()
    }
    @get:Synchronized var maxRequestsPerHost = 5
    set(maxRequestsPerHost) {
      require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
      synchronized(this) {
        field = maxRequestsPerHost
      }
      promoteAndExecute()
    }
  	//异步请求
    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()
  }
  //同步请求
  /** Used by [Call.execute] to signal it is in-flight. */
  @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }
    private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()
    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
        //Max capacity.
        if (runningAsyncCalls.size >= this.maxRequests) break 
        //Host max capacity.
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue 
        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
  }
}

根据注释的信息可以知道,Dispatcher是处理异步请求的执行的策略,当然开发可以实现自己的策略。

Policy on when async requests are executed.
Each dispatcher uses an ExecutorService to run calls internally. If you supply your own executor, it should be able to run the configured maximum number of calls concurrently.

知道了Dispatcher的作用,再回到client.dispatcher.executed(this),也即:

  /** Used by [Call.execute] to signal it is in-flight. */
  @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

结合execute()Dispatcher分析

  • Dispatcher主要是对异步请求的调度作用,内部实现了线程池,对请求进行分发执行。
  • 包含三个双端队列分别为等待的异步队列readyAsyncCalls、执行中的队列runningAsyncCalls、执行中的同步队列runningSyncCalls。这里使用ArrayDeque,为什么不使用LinkedList有兴趣的可以思考一下。
  • 当请求为同步请求时,内部执行execute()RealCall添加到runningSyncCalls队列中。

到这里请求其实还没有真正的执行,只是在做一些前期的工作,回到Call接口中看看对方法同步请求方法execute()的说明:同步请求可以立即执行,阻塞直到返回正确的结果,或者报错结束。

Invokes the request immediately, and blocks until the response can be processed or is in error.

#4步执行后,return getResponseWithInterceptorChain() //#5这个方法才是请求一步步推进的核心。也是okhttp网络请求责任链的核心模块。

三、链的开启-getResponseWithInterceptorChain()
  • getResponseWithInterceptorChain()具体实现
  @Throws(IOException::class)
  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()方法之前有必要看看OkHttpClient的构造参数,使用的Builder模式,参数很多,可配置化的东西很多,精简一下主要关注几个参数:

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 cache: Cache? = null
  //代理
  internal var proxy: Proxy? = null
  //链接协议
  internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
  //.......
}
//添加自定义拦截器的方法
fun addInterceptor(interceptor: Interceptor) = apply {
  interceptors += interceptor
}
//添加自定义网络拦截器的方法
fun addNetworkInterceptor(interceptor: Interceptor) = apply {
  networkInterceptors += interceptor
}

到这里有个疑问,这个添加自定义拦截器与添加自定义网络拦截器有什么区别呢?方法看上去是差不多的,查看官方的说明可以发现一些细节。文档中解释了Application InterceptorNetwork Interceptors的细微差别。先回到RealCall中查看getResponseWithInterceptorChain()是如何对拦截器结合组装的:

internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf<Interceptor>()
  interceptors += client.interceptors //#1
  interceptors += RetryAndFollowUpInterceptor(client)
  interceptors += BridgeInterceptor(client.cookieJar)
  interceptors += CacheInterceptor(client.cache)
  interceptors += ConnectInterceptor
  if (!forWebSocket) {
    interceptors += client.networkInterceptors//#2
  }
  interceptors += CallServerInterceptor(forWebSocket)
}

#1#2分别对应添加的自定义拦截器与自定义网络拦截器的位置,自定义拦截器是拦截器链的链头,而自定义网络拦截器在ConnectInterceptor拦截器与CallServerInterceptor拦截器之间被添加。总结一下:

  • 自定义拦截器在整个拦截器链的头部,做一些请求的之前的准备工作,包括一些Log的信息打印,如LoggingInterceptor

  • 自定义网络拦截器添加在ConnectInterceptorCallServerInterceptor之间,可以执行两次,并且获得的信息更多,包括原网址的信息和重定向后网址的信息。

  • Application interceptors

Don’t need to worry about intermediate responses like redirects and retries.

Are always invoked once, even if the HTTP response is served from the cache.

Observe the application’s original intent. Unconcerned with OkHttp-injected headers like If-None-Match.

Permitted to short-circuit and not call Chain.proceed().

Permitted to retry and make multiple calls to Chain.proceed().

Can adjust Call timeouts using withConnectTimeout, withReadTimeout, withWriteTimeout.

Able to operate on intermediate responses like redirects and retries.

Not invoked for cached responses that short-circuit the network.

Observe the data just as it will be transmitted over the network.

Access to the Connection that carries the request.

在这里插入图片描述
综上可以得出整个链的顺序结构,如果都包含自定义拦截器与自定义网络拦截器,则为自定义拦截器->RetryAndFollowUpInterceptor->BridgeInterceptor->CacheInterceptor->ConnectInterceptor->自定义网络拦截器->CallServerInterceptor;那么链是如何按照顺序依次执行的呢?okhttp在这里设计比较精妙,在构造RealInterceptorChain对象时带入index信息,这个index记录的就是单个拦截器链的位置信息,而RealInterceptorChain.proceed(request: Request)通过index++自增一步步执行责任链一直到链尾。

val chain = RealInterceptorChain(
  call = this,
  interceptors = interceptors,
  //记录了拦截器对应的index通过对
  index = 0,
  exchange = null,
  request = originalRequest,
  connectTimeoutMillis = client.connectTimeoutMillis,
  readTimeoutMillis = client.readTimeoutMillis,
  writeTimeoutMillis = client.writeTimeoutMillis
)
val response = chain.proceed(originalRequest)
四、proceed链的推进
  • RealInterceptorChain中方法proceed()方法的具体实现
@Throws(IOException::class)
override fun proceed(request: Request): Response {
  check(index < interceptors.size)
  calls++
  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"
   }
  }
  // Call the next interceptor in the chain.
  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")
  //.....
  return response
}

简单的分析推进过程:

1.RealInterceptorChain的构造参数中携带了index的信息,index++自增通过proceed方法不断执行。

2.拦截器统一实现Interceptor接口,接口中fun proceed(request: Request): Response保证了链式链接。当然拦截器的顺序是按照一定的规则排列的,逐个分析。

五、RetryAndFollowUpInterceptor重试与重定向
companion object {
  private const val MAX_FOLLOW_UPS = 20
}
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  var request = chain.request
  val call = realChain.call
  var followUpCount = 0
  var priorResponse: Response? = null
  var newExchangeFinder = true
  var recoveredFailures = listOf<IOException>()
	while (true) {
    //....
    try {
      response = realChain.proceed(request)
      newExchangeFinder = true
    }
  }
  //....
}

1.重试拦截器规定默认的重试次数为20次

2.以response = realChain.proceed(request)为分界点,包括其他的拦截器,在责任链传递之前所做的工作都是前序工作,然后将request下发到下一个拦截器。

3.response = realChain.proceed(request)后的代码逻辑为后续工作,即拿到上个拦截器的response结果,有点递归的意思,按照责任链的执行一直到最后一个拦截器获得的结果依次上抛每个拦截器处理这个response完成一些后序工作。

4.当然并不是每个请求都会走完整个链,如CacheInterceptor当开启了缓存(存在缓存)拿到了缓存的response那么之后的拦截器就不会在继续传递。

六、BridgeInterceptor
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")
    }
  }
  //.....
  //分界点,上部分为前序操作
  val networkResponse = chain.proceed(requestBuilder.build())
  //下部分代码为拿到response的后序操作
  cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
}

1.桥接拦截器主要对请求的Hader的信息的补充,包括内容长度等。

2.传递请求到下一个链,等待返回的response信息。

3.后序的操作包括Cookiegzip压缩信息,User-Agent等信息的补充。

七、CacheInterceptor
override fun intercept(chain: Interceptor.Chain): Response {
  val call = chain.call()
  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
  //......
  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()
   }
  }
  //.....
}
  • OkHttpClient的默认构造中是没有开启缓存的;internal var cache: Cache? = null,需要手动添加,但是有默认实现,基于最近最少使用算法实现的DiskLruCache磁盘缓存:
private val client: OkHttpClient = OkHttpClient.Builder()
  .cache(Cache(
    directory = File(application.cacheDir, "http_cache"),
    // $0.05 worth of phone storage in 2020
    maxSize = 50L * 1024L * 1024L // 50 MiB
  ))
  .build()

1.缓存拦截器默认没有被开启,需要在调用时指定缓存的目录,内部基于DiskLruCache实现了磁盘缓存。

2.当缓存开启,且命中缓存,那么链的调用不会再继续向下传递(此时已经拿到了response)直接进行后序的操作。

3.如果未命中,则会继续传递到下一个链也即是ConnectInterceptor

八、ConnectInterceptor
/**
 * Opens a connection to the target server and proceeds to the next interceptor. 
 * The network might be used for the returned response, 
 * or to validate a cached response with a conditional GET.
 */
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)
    return connectedChain.proceed(realChain.request)
  }
}
  • ConnectInterceptor的代码量很少主要作用很清晰。

1.建立与目标的服务器的TCP或者TCP-TLS的链接。

2.与之前的拦截器不同,前面的拦截器的前序操作基于调用方法realChain.proceed()之前,但是ConnectInterceptor 没有后序操作,下发到下一个拦截器

九、CallServerInterceptor
@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()) {
         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 (!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) {
      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) {
    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
  }
}
  • CallServerInterceptor是整个责任链的尾链:

1.实质上是请求与I/O操作,将请求的数据写入到Socket中。

2.从Socket读取响应的数据TCP/TCP-TLS对应的端口,对于I/O操作基于的是okio,而okhttp的高效请求同样离不开okio的支持。

3.拿到数据reponse返回到之前包含有后序操作的拦截器,但ConnectInterceptor除外,ConnectInterceptor是没有后续操作的。

整个拦截器流程图如下:
拦截流程图

十、ReaclCall.execute()
  • 回头看同步请求的方法:
  override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    timeout.enter()
    callStart()
    try {
      client.dispatcher.executed(this)
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }
  • Dispatcher中对于同步的请求仅仅只是将,RealCall的实例添加到runningSyncCalls.add(call)执行中的队列中。
  • RealCallCall的唯一实现类,而对于execute()方法的定义明确了,立即执行阻塞直到response结果的返回或者error信息打断阻塞。
  • 经过**getResponseWithInterceptorChain()的调用,链的推进此时已经拿到结果,那么后序的操作是什么呢?先看一个Java基础try{}finally{}**结构,以及dispatcher.finished(call: RealCall)
try{
  client.dispatcher.executed(this)
  return getResponseWithInterceptorChain()
} finally{
  client.dispatcher.finished(this)
}

/** Used by [Call.execute] to signal completion. */
internal fun finished(call: RealCall) {
  finished(runningSyncCalls, call)
}

1.排除极端的情况,System.exit()或者其他,finally 块必然执行,不论发生异常与否,也不论在 finally 之前是否有return

2.不管在 try 块中是否包含 return,finally 块总是在 return 之前执行。

3.如果 finally 块中有return,那么 try 块和 catch 块中的 return就没有执行机会了。

Tip:第二条的结论很重要,回到execute()方法,dispatcher.finished(this)在结果response结果返回之前执行,看finished()具体实现。

  /** Used by [Call.execute] to signal completion. */
  internal fun finished(call: RealCall) {
    finished(runningSyncCalls, call)
  }
  private fun <T> finished(calls: Deque<T>, call: T) {
    val idleCallback: Runnable?
    synchronized(this) {
      //#1
      if(!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
      idleCallback = this.idleCallback
    }
    //#2
    val isRunning = promoteAndExecute()
    if (!isRunning && idleCallback != null) {
      idleCallback.run()
    }
  }

  private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()
    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      //#3
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
				// Max capacity.
        if (runningAsyncCalls.size >= this.maxRequests) break 
        // Host max capacity.
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue 
        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        executableCalls.add(asyncCall)
        runningAsyncCalls.add(asyncCall)
      }
      //#4
      isRunning = runningCallsCount() > 0
    }
    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)
    }
    //#5
    return isRunning
  }
	
 @Synchronized fun runningCallsCount(): Int = runningAsyncCalls.size + runningSyncCalls.size

1.#1方法一,calls.remove(call)返回为true,也即是这个同步请求被从runningSyncCalls中移除释放;所以idleCallback为空。

2.#3很显然asyncCall的结果为空,没有异步请求,在看#4具体实现,runningSyncCallssize为1。则isRunning的结果为true。idleCallback.run()不会被执行,并且idleCallback其实也是为空。

  • 至此,开始的例子所构建的同步请求就被执行完毕了,阻塞拿到了结果回调,用于页面展示或者其他操作。
  • 梳理整个流程如下:

1.从OkHttpClient().newCall(request).execute() 开启同步请求任务。

2.得到的RealCall对象作为Call的唯一实现类,其中同步方法execute()是阻塞的,调用到会立即执行阻塞到有结果返回,或者发生错误error被打断阻塞。

3.RealCall中同步execute()请求方法被执行,而此时OkHttpClient实例中的异步任务分发器Dispatcher会将请求的实例RealCall添加到双端队列runningSyncCalls中去。

4.通过RealCall中的方法getResponseWithInterceptorChain()开启请求拦截器的责任链,将请求逐一下发,通过持有index 并自增操作,其次除ConnectInterceptor与链尾CallServerInterceptor其余默认拦截器均有以chain.proceed(request)为分界点的前序与后序操作,拿到response后依次处理后序操作。

5.最终返回结果response之前,对进行中的同步任务做了移除队列的操作也即finallyclient.dispatcher.finished(this)方法,最终得到的结果response返回到客户端,至此整个同步请求流程就结束了。

十一、异步请求的处理enqueue()
  • 异步请求的大致流程与同步请求是差不多的,但是多了一个从等待队列到执行队列转化的一个过程。
   internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
    //#1
    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)
      }
    }
    //#2
    promoteAndExecute()
  }

 private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()
    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        //#3
        val asyncCall = i.next()
				// Max capacity.
        if (runningAsyncCalls.size >= this.maxRequests) break 
        // Host max capacity.
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue 
        //#4
        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        executableCalls.add(asyncCall)
        //#5
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }
    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)
    }
    return isRunning
  }
  • #1方法中将异步请求任务加入到readyAsyncCalls等待队列之中,#2开启执行任务(当然Dispatcher维护了线程池来处理这些请求)。
  • 执行方法promoteAndExecute()#3将异步任务从等待队列readyAsyncCalls中取出并移除操作,#5则添加到执行中的异步任务队列runningAsyncCalls,如此直到所有请求全部执行完毕。
十二、小结
  • 这里仅仅是对大概的流程简单的作出了分析,忽略了一些其他的实现细节,像请求缓存池、请求的协议。包括本地缓存DiskLruCache的实现细节,以及异步请求的一些阀值的判断处理。在精力允许的情况下还应该仔细研究一番,毕竟从Java到现在的Kotlin版本okhttp经历了很多的迭代也更加成熟,当然网络请求这块okhttp的地位还是无法撼动的🐶。
十三、文档

Github

Square

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐二狗呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值