okhttp是一个老牌的网络请求框架,这里是它的官网地址
具体的使用方法也可以参考官网地址
官网说默认的http客户端他有下面几个特点
- HTTP / 2支持允许对同一主机的所有请求共享一个套接字。
- 连接池可减少请求延迟(如果HTTP / 2不可用)。
- 透明的GZIP缩小了下载大小。
- 响应缓存可以完全避免网络重复请求。
今天就来看看什么个它。
首先还是来看看它的整体架构(v4.4.0 版本)。
重点看几个类
OkhttpClient是一个对外使用的类,内部核心实现是RealCall.
RealCall是okhttp进行网络访问的核心现类,它实现了网络请求的逻辑。
Dispatcher 是一个任务调度的类,它内部维护三个请求队列和一个线程池,是任务的执行的管理者。
AsyncCall是一个runnable类,内部装了request请求和respone派发逻辑。
Interceptor是拦截器类,它的子类中分装了request处理和resonse处理的逻辑,okhttp中有很多拦截器。
接下来看异步消息的执行流程
这里可以看任务的派发是交给Dispatcher对象的。Dispatcher中定义了三个队列,准备执行异步队列(readyAsyncCalls),正在执行的异步队列(RunningAsycCalls),正在执行的同步请求队列(runningSyncCalls),任务队列维护的过程稍后再看。这里先来看一下线程程池对象executorService。
@get:Synchronized var maxRequests = 64
set(maxRequests) {
require(maxRequests >= 1) { "max < 1: $maxRequests" }
synchronized(this) {
field = maxRequests
}
promoteAndExecute()
}
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
这里使用比较特殊的无界线程池。线程池中使用的是阻塞队列SynchronousQueue,该队列中不会存数据,每次存入数据的时候必须等另外一个线程取完数据,所以线程池中不会存任务。
线程池的容量是无穷大(Int.MAX_VALUE)并且没有核心任务,每次新任务到来就新开线程,那如果任务很多并且耗时较长的时候,会不会出现oom呢?
不会,上面提到两个队列,readAsyncCalls和runningAsyncCalls ,其中runningAsyncCalls中任务的数量就是线程池中正在运行的线程个数,它容量被参数maxRequests(默认值是64)限制,新到的异步任务会直接加入到readAsyncCalls,然后从readAsyncCalls中取出来,如果runningAsyncCalls中的任务数量小于64,就从readAsyncCalls中转移到runningAsyncCalls中,然后把该任务投递到executorService中执行。如果runningAsyncCalls的任务数大于等于64,就暂时不转移当前任务的所属队列,不投递到线程池中。当该任务执行完毕的时候,就会把该请求从runningAsyncCalls中删除,然后从readAsyncCalls中开启新一轮的转移、投递行为。runningAsyncCalls中存储的任务最多为64,所以线程池中最多有64个并发任务,内存占用较小,不会出现oom。
什么?为啥不用固定线程池容量呢?
如果固定线程是容量,这个线程数大小很难确定,同时如果线程容量定的小了,还需要指定任务拒绝策略。采用上述方式,每个任务都能得到执行,不需要定制拒绝策略。
任务添加的简单流程
值得注意的是Dispatcher.finished(Call)中会执行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
}
拦截器的运行方式
拦截器机制要从RealCall的 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)
}
}
}
可以看到这里使用MutableList数据结构 构建了一个Interceptor栈结构,注意RealInterceptorChain的够在方法中的index参数,它是递归核心参数,现在传递的是0,可以把它看作连接器在MutableList中的序号。
接下来是重要的递归调用。RealInterceptorChain的proceed(originalRequest)
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
......
// 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")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
internal fun copy(
index: Int = this.index,
exchange: Exchange? = this.exchange,
request: Request = this.request,
connectTimeoutMillis: Int = this.connectTimeoutMillis,
readTimeoutMillis: Int = this.readTimeoutMillis,
writeTimeoutMillis: Int = this.writeTimeoutMillis
) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,
readTimeoutMillis, writeTimeoutMillis)
上面重点看
val next = copy(index = index + 1, request = request)方法。
假设copy()方法重新构造了一个新的RealInterceptorChain简单记作RealInterceptorChainA, 更新了index = 0+1 = 1,和request,此时的request是最原始的requst,就是getResponseWithInterceptorChain()方法中的那个originRequest。
然后取得当前的序号index= 0的拦截器,执行它的拦截方法Intercept()。也就是MutableList中第一个添加的拦截器(client.interceptors)。
记第一个拦截器为client.interceptors,在client.interceptors的Intercept()方法中会对request相关的处理,修改完的request记作request1,然后调用RealInterceptorChainA的proceed(request),注意这里proceed方法中的request就是request1。处理过的request1就传递给了下一级调用。此时的RealInterceptorChainA的index=1。
开启下一轮循环。流程和上面类似,RealInterceptorChainA的proceed()中生成新的拦截器记作RealInterceptorChainB,它的index=1+1,request是上面传过来的request1;根据RealInterceptorChainA中index取得拦截器,index= 1的拦截器(RetryAndFollowUpInterceptor)。执行他的intercept()方法...
伴随着index的增加,MutableList中的每一个拦截器都能调用。一直到最后一个拦截器CallServerInterceptor返回response,结束d递归过程,函数调用栈中函数开始出栈,对response做拦截处理。所有的拦截器都对response做完处理就能够派发结果了。
拦截器调用栈如下图所示。
链接池ConnectionPool,实际类是RealConnection。
如果多个http请求是相同的请求地址,那么它的链接可以复用。链接池会保持五个空闲连接等待复用,每个连接的保持事件是5分钟。复用过程在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)
return connectedChain.proceed(realChain.request)
}
}
其中Exchange对象请求和响应的传输层,initExchange(chain)方法完成了连接池的复用的入口。该对象生成的连接会优先从连接池中查找,如果没有就新建连接。
ExchangeCodec接口管理了Http编码和解码(可以用Connection生成)。ExchangeFinder.findConnection()优先从链接池中找同意主机的连接,弱国找到就直接复用,如果没有找到就新建一个,然后存入链接池并返回该连接。
欢迎指出错误,本文长期维护更新。