前面我们提到了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源码分析(三)。