OkHttp源码分析(二)

前面我们提到了OkHttp整体的流程,从真正的请求发出者RealCall去调用同步或者异步方法,完成网络请求。对整体流程不清楚的同学可以点击这个链接OkHttP源码分析(一)直达哦。

通过前面的分析我们也知道,调用了同步或异步方法并不会立即去执行网络请求,而是会经过分发器的再一步处理,才会执行。

接下来我们就去源码里看看吧。

直接看同步方法的调用。

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

我们可以看到client调用了dispatcher的executed方法。

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

executed里面直接把当前的call添加到runningSyncCalls了。那么这个runningSyncCalls是何方神圣呢。

我们仔细去看看Dispatcher类里面的变量吧。

//最大请求数
var maxRequests = 64
//同一主机允许访问最大连接数
var maxRequestsPerHost = 5
//每次调度程序空闲时(当正在运行的调用数归零时)调用的回调
var idleCallback: Runnable? = null
private var executorServiceOrNull: ExecutorService? = null
val executorService: ExecutorService
/** 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>()

我把全部变量都列出来了。一眼看下去就很清晰。注释也都很详细,这里也就知道了runningSyncCalls所代表的意思,同步执行的队列。

我们再看一下,异步执行的队列是在哪里被赋值的。这时候聪明的同学肯定知道了,肯定在异步执行的时候被赋值的。看看吧。

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

先把传入进来的call加入readyAsyncCalls,再去判断访问的是不是同一个地址。异步方法里有三个方法,findExistingCallWithHost()、reuseCallsPerHostFrom()、 promoteAndExecute(),我们一一分析。

private fun findExistingCallWithHost(host: String): AsyncCall? {
  for (existingCall in runningAsyncCalls) {
    if (existingCall.host == host) return existingCall
  }
  for (existingCall in readyAsyncCalls) {
    if (existingCall.host == host) return existingCall
  }
  return null
}

通过host这个属性来判断的,找到了就直接返回。返回之后再怎么进行处理呢。

往下看看。

@Volatile var callsPerHost = AtomicInteger(0)
  private set

fun reuseCallsPerHostFrom(other: AsyncCall) {
  this.callsPerHost = other.callsPerHost
}

会调用 AsyncCall里面的reuseCallsPerHostFrom,对这个callsPerHost变量加一。

我们再看看promoteAndExecute()。

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

      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
}

看起来一大堆,其实就是判断最大请求数是不是到了,请求同一地址的请求数是不是到了。然后把ready队列里的call拿出来添加到run队列里了。这就是这个方法的最主要作用了。

不知道看到这里大家有没有疑问啊。为啥异步需要两个队列,而同步只需要一个队列。这是因为同步不涉及多个线程的情况,只要一个队列来管理请求,处理请求。异步则涉及多个线程,所以需要两个队列来管理。

到这里关于分发器的核心代码都分析完成了。欢迎期待OkHttp源码分析(三)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值