OkHttp源码解析
1 OkHttp介绍与用法示例
1.1 OkHttp简介
Http是现代应用程序的网络请求方式,也是应用进行数据交互和流媒体播放的方式,高效的Http数据交互可以让我们的应用加载更快,同时能够节省宽带费用。OkHttp正是这样的一个高效Http客户端,其中OkHttp的项目地址为:github.com/square/okht...
可以看到,OkHttp天然就有以下优点(敲黑板,划重点了,选择OkHttp的好处):
- 由于支持Http协议,可以让所有的请求指向同一个host,分享同一个socket
- 链接池化技术降低了请求延时
- Gzip压缩减少了下载数据量
- 请求结果缓存避免了重复的数据请求
在网络存在异常的时候,OkHttp会自动处理一些常见的网络请求异常问题。如果我们请求的服务存在多个IP且第一次连接失败的时候,OkHttp会自动替换我们请求的IP。同时OkHttp支持TLS(Transport Layer Security,安全传输层协议)。
1.2 OkHttp的用法示例
首先还是依赖的导入,在项目的配置文件中增加以下依赖:
kotlin
implementation("com.squareup.okhttp3:okhttp:5.1.0")
当前最新的版本是5.1.0,需要使用新版本的可以去项目地址看一下最新的版本。然后是使用方法介绍:
java
// 创建client实例
OkHttpClient client = new OkHttpClient();
public static final MediaType JSON = MediaType.get("application/json");
// get请求
String run(String url) throws IOException {
// 创建request请求
Request request = new Request.Builder()
.url(url)
.build();
// 同步执行
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
// post请求
String post(String url, String json) throws IOException {
// 创建请求体
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
// 同步的方式执行请求
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
上面介绍的方式是使用同步的方式执行,实际的开发过程中通常是以异步回调的方式进行http请求的,下面给出一个异步回调方式请求服务端接口示例:
java
// get请求
String run(String url) throws IOException {
// 创建request请求
Request request = new Request.Builder()
.url(url)
.build();
// 异步执行
client.newCall().enqueue(new Callback(){
@override
void onResponse(Call call,Response response){
// todo 接口返回处理
}
@override
void onFail(Call call,IOException e){
// todo 接口异常处理
}
})
}
可以看到异步的请求的时候,使用的是okhttp3.internal.connection.RealCall#enqueue方法,同时需要传入一个回调监听,根据回调返回的接口进行数据的处理。无论是同步的方式还是异步的方式,进行http请求的方式都是很简单,那么这些请求代码里OkHttp都在后面默默做了些什么呢,接下来看看代码的主流程分析。
2 OkHttp源码实现分析
2.1 OkHttp主流程分析
- 从1.2中给出的代码示例来看,主要的入口还是在okhttp3.OkHttpClient类中,该类中提供了一个okhttp3.OkHttpClient.Builder类,用于配置OkHttp的各种配置、监听等属性,具体的可配置项还是挺多的,具体的每一项及其用途这里就不一一列举了,其中有几个重要如interceptors、读写超时配置、重试机制等配置。获取到OkHttpClient实例(在实际的项目中通常是将创建OkHttp实例封装起来放在一个单例里面,在需要的地方直接使用相关方法获取)后,无论是同步执行还是异步的方式调用都用到了okhttp3.OkHttpClient#newCall方法,这个方法返回了一个okhttp3.internal.connection.RealCall实例,以同步方式为例,走到了okhttp3.internal.connection.RealCall#execute方法,代码如下:
kotlin
override fun execute(): Response {
// a、检查执行状态
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
// b、通知eventListener接口调用
callStart()
try {
// c、 将当前call实例添加到okhttp3.Dispatcher#runningSyncCalls队列
client.dispatcher.executed(this)
// d、 大名鼎鼎的拦截器模式
return getResponseWithInterceptorChain()
} finally {
// e、 执行调度器的下一轮处理
client.dispatcher.finished(this)
}
}
- 从流程1给出的代码看,主要做了5件事情,其中client.dispatcher.executed(this)将当前的RealCall实例加入到Dispatcher的runningSyncCalls调度队列中,重点看看okhttp3.internal.connection.RealCall#getResponseWithInterceptorChain方法中做了些什么,其实现代码如下:
kotlin
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)
}
}
}
- 根据流程2列出来的代码中,可以看到责任链中除了自定义的拦截器外,OkHttp框架本身提供了5个默认的拦截器分别是:
- RetryAndFollowUpInterceptor
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- CallServerInterceptor
这五个拦截器在okhttp3.internal.http.RealInterceptorChain中如何被调用的,我们来一起看下okhttp3.internal.http.RealInterceptorChain#proceed方法中做了些什么:
kotlin
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
// 流程2的代码19行可以看到exchange为null,这里直接跳过不执行
if (exchange != null) {
check(exchange.finder.routePlanner.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.
// 通过copy方法传入index+1,得到下一个拦截器
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
// 下面的interceptor变量是索引为index的拦截器
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"
}
}
return response
}
- 在流程3中的代码里,重点关注索引为0的拦截器运行的 okhttp3.Interceptor#intercept方法,假如我们在初始化OkHttpClient实例的时候没有传入自定义的拦截器,那么拦截器数组interceptors中只会有框架默认的拦截器,也就是上面流程3中列出来的5个默认的拦截器,下面按照拦截器顺序依次看各个拦截器的实现(拦截器实现了okhttp3.Interceptor#intercept方法)。首先看下RetryAndFollowUpInterceptor拦截器的intercept方法:
kotlin
override fun intercept(chain: Interceptor.Chain): Response {
// a、初始化各个变量,其中重点关注newRoutePlanner值为true
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newRoutePlanner = true
var recoveredFailures = listOf<IOException>()
while (true) {
// b、设置okhttp3.internal.connection.RealCall#exchangeFinder的值
call.enterNetworkInterceptorExchange(request, newRoutePlanner, chain)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
// c、重复流程3中贴出的proceed代码,进入下一个拦截器的intercept方法处理逻辑
response = realChain.proceed(request)
newRoutePlanner = true
} catch (e: IOException) {
// An attempt to communicate with a server failed. The request may have been sent.
// d、收到异常后,通知eventListener,退出循环
val isRecoverable = recover(e, call, request)
call.eventListener.retryDecision(call, e, isRecoverable)
if (!isRecoverable) throw e.withSuppressed(recoveredFailures)
recoveredFailures += e
newRoutePlanner = false
continue
}
// Clear out downstream interceptor's additional request headers, cookies, etc.
response =
response
.newBuilder()
.request(request)
.priorResponse(priorResponse?.stripBody())
.build()
// e、接受用户自定义的拦截器中返回的Response,并填充相应的验证标签信息,处理重定向
val exchange = call.interceptorScopedExchange
val followUp = followUpRequest(response, exchange)
// f、followUp为空表示,follow-up不必要或不可达,直接退出
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
call.eventListener.followUpDecision(call, response, null)
return response
}
// g、followUpBody为一次性的请求,直接返回当前response
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
call.eventListener.followUpDecision(call, response, null)
return response
}
response.body.closeQuietly()
// h、超过最大的follow-up次数,直接抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
call.eventListener.followUpDecision(call, response, null)
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
// i、更新当前的follow-up请求和返回,继续从b处开始循环
call.eventListener.followUpDecision(call, response, followUp)
request = followUp
priorResponse = response
} finally {
// j、退出当前重试机制,设置标志位
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
代码看起来很多,重点的步骤是b、c,在循环内处理了重试和重定向,笔者都在对应位置做了代码注释。其中注释b处的处理在后续的ConnectInterceptor拦截器中还会用到。
5、接下来看看BridgeInterceptor中做了些什么:
kotlin
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
// a、此处省略了一系列的cookie和网络请求头的设置
// b、进入下一个拦截器的处理环节
val networkRequest = requestBuilder.build()
val networkResponse = chain.proceed(networkRequest)
cookieJar.receiveHeaders(networkRequest.url, networkResponse.headers)
val responseBuilder =
networkResponse
.newBuilder()
.request(networkRequest)
// c、处理Gzip压缩的特殊情况
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()
) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders =
networkResponse.headers
.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
可以看出BridgeInterceptor主要是处理一些和cookie相关的请求头设置,Gzip协议的特殊处理。整体相对简单。
- 接下来看看CacheInterceptor里面做了一些什么处理,代码如下:
kotlin
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request().requestForCache())
val now = System.currentTimeMillis()
// a、通过compute方法得到一个关于缓存的数据结构CacheStrategy
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
// b、通过DiskLruCache中获取的cacheCandidate与compute得到的缓存都存在,
// 关闭获取自DiskLruCache中拿到的cacheCandidate
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body.closeQuietly()
}
// If we're forbidden from using the network and the cache is insufficient, fail.
// c、通过compute方法返回的CacheStrategy,缓存失效,但网络不可用,返回状态码504网关超时
if (networkRequest == null && cacheResponse == null) {
return Response
.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
.also {
listener.satisfactionFailure(call, it)
}
}
// If we don't need the network, we're done.
// d、通过compute方法返回的CacheStrategy中,只有在CacheStrategy.Factory#computeCandidate
// 方法中,允许访问缓存且缓存未过期(涉及几个缓存时间的计算),则直接返回CacheStrategy的缓存
if (networkRequest == null) {
return cacheResponse!!
.newBuilder()
.cacheResponse(cacheResponse.stripBody())
.build()
.also {
listener.cacheHit(call, it)
}
}
// 通过监听回调通知缓存命中情况
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
// e、进入下一个拦截器的处理,实际上就是真正走网络请求了
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()
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
// f、缓存不为空且网络数据的code为HTTP_NOT_MODIFIED,更新缓存的相关标签
// headers、时间戳等数据,并更新LruDiskCache中的数据,同时通知回调缓存命中
val response =
cacheResponse
.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(cacheResponse.stripBody())
.networkResponse(networkResponse.stripBody())
.build()
networkResponse.body.close()
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body.closeQuietly()
}
}
// g、使用网络返回数据并更新LruDiskCache缓存
val response =
networkResponse!!
.newBuilder()
.cacheResponse(cacheResponse?.stripBody())
.networkResponse(networkResponse.stripBody())
.build()
if (cache != null) {
val cacheNetworkRequest = networkRequest.requestForCache()
if (response.promisesBody() && CacheStrategy.isCacheable(response, cacheNetworkRequest)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response.newBuilder().request(cacheNetworkRequest).build())
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
// h、更新由于特殊的网络请求引起的LruDiskCache中的缓存
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
这部分其实是实际面试中喜欢问到的(划重点了),主要涉及到http的一些缓存知识,在主流程里面未深入去讲http里面的eTag、lastModified标签。先留个坑,在后面仔细讲一下这一块知识点。
- 接下来看看ConnectInterceptor拦截器的实现:
kotlin
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
// a、在流程4的注释b处,初始化了exchangeFinder,在这个方法内部会用到,这里很重要
val exchange = realChain.call.initExchange(realChain)
// b、更新realChain中的exchange值
val connectedChain = realChain.copy(exchange = exchange)
// c、进入下一个拦截器的处理逻辑内
return connectedChain.proceed(realChain.request)
}
注释a处会调用okhttp3.internal.connection.RealCall#enterNetworkInterceptorExchange中初始化的exchangeFinder,正常没在初始化OkHttpClient初始化的时候配置过的话,默认会返回FastFallbackExchangeFinder,如果配置了okhttp3.OkHttpClient.Builder#fastFallback变量为false的话,就会返回SequentialExchangeFinder类型。这两个类都实现了find接口,调用者通过find方法返回的RealConnection进行数据交互。这两个类的实现先留个坑,先继续我们的主流程。
- CallServerInterceptor拦截器的实现如下:
kotlin
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()
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
var sendRequestException: IOException? = null
try {
// a、向服务器发送请求
exchange.writeRequestHeaders(request)
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
// b、源码的注释意思是,请求里面有"Expect:100-continue",需要等待一个
// 完整的"HTTP/1.1 100 Continue"请求后才会真正发送请求体。如果我们没正常收
// 到"HTTP/1.1 100 Continue"的服务端返回,将不会发送请求体,且会受到一个4XX的返回码
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()) {
// Prepare a duplex body so that the application can send a request body later.
// b、双工模式下准备请求体并写入(http2才支持的协议)
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
// c、非双工模式下,准备请求体并写入
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
// d、未满足状态码100,返回的responseBuilder不为空,通知请求结束,防止当前连接重用
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
// e、请求发送结束
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
} catch (e: IOException) {
if (e is ConnectionShutdownException) {
throw e // No request was sent so there's no response to read.
}
if (!exchange.hasFailure) {
throw e // Don't attempt to read the response; we failed to send the request.
}
sendRequestException = e
}
try {
if (responseBuilder == null) {
// f、读取返回的请求头
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())
.bui读取
var code = response.code
// g、如果返回码为100、102~199,都循环读取
while (shouldIgnoreAndWaitForRealResponse(code, exchange)) {
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)
// h、打开请求的请求体,结束整个请求过程
response =
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.stripBody()
} 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() > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body.contentLength()}",
)
}
return response
} catch (e: IOException) {
if (sendRequestException != null) {
sendRequestException.addSuppressed(e)
throw sendRequestException
}
throw e
}
}
至此整个请求的主流程就结束了,其实还是埋下了不少细节上的坑。可以看出来整个主流程里面其实没太涉及到Socket底层相关的源码,这是因为OkHttp中将更加底层的Socket代码细节隐藏了,我们在源码的主流程中没有涉及到这一块,其实还有缓存相关的处理细节、OkHttp请求任务调度细节等。接下来就看看留下来的坑,开始填坑工作。
2.2 OkHttp源码加载细节
在前面的主流程分析中,我们介绍了整个请求的创建过程,从OkHttpClient配置到生成一个新的RealCall执行,最终进入OkHttp大名鼎鼎的拦截器模式处理,由5个OkHttp的默认拦截器依次处理整个请求流程,在2.1我们分析了整个主流程和5个拦截器的实现。本节一起来看看隐藏在主流程中的实现细节。
2.2.1 OkHttp任务调度实现
在1.2中我们介绍了任务执行的两种方式,同步的方式okhttp3.internal.connection.RealCall#execute,该方法实现如下:
kotlin
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)
}
}
跟进okhttp3.Dispatcher#executed方法内部实现你会发现很简单,实现如下:
kotlin
@Synchronized
internal fun executed(call: RealCall) = runningSyncCalls.add(call)
加了锁的同步方法,往Dispatcher内部的runningSyncCalls添加一个RealCall实例。继续看看异步方法okhttp3.internal.connection.RealCall#enqueue方法,同样也是调用了Dispatcher的enqueue方法,我们直接看一下Dispatcher的enqueue方法:
kotlin
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()
}
可以看到enqueue方法将AsynCall实例加入到readyAsyncCalls队列内,需要注意的是equeue方法内代码块加了锁。如果不是一个WebSocket请求,会根据请求的host在异步队列readyAsyncCalls和runningAsyncCalls中寻找已存在的请求,并重用找到的call。实际RealCall的reuseCallsPerHostFrom方法是更新了一下每个host主机上存在的call请求数量。在讲重点的okhttp3.Dispatcher#promoteAndExecute方法之前,我需要介绍一下几个Dispatcher的变量:
- maxRequests 最大并发请求量(默认值为64)
- maxRequestPerHost 单个host主机的最大并发请求量(默认值为5)
- readyAsyncCalls 等待队列中的异步请求列表
- runningAsyncCalls 正在执行的异步请求列表
- runningSyncCalls 正在执行的同步请求
了解了以上的几个变量之后,再来看promoteAndExecute方法的实现就很简单了:
kotlin
private fun promoteAndExecute(): Boolean {
assertLockNotHeld()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
// a、遍历readyAsyncCalls队列,找出满足条件的可执行AsyncCall
// 实例(不超过maxRequests和maxRequestsPerHost)
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
}
// Avoid resubmitting if we can't logically progress
// particularly because RealCall handles a RejectedExecutionException
// by executing on the same thread.
if (executorService.isShutdown) {
// b、处理executorService处于shutdown状态的数据处理
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.callsPerHost.decrementAndGet()
synchronized(this) {
runningAsyncCalls.remove(asyncCall)
}
asyncCall.failRejected()
}
idleCallback?.run()
} else {
// c、遍历执行注释a处找到的可执行AsyncCall实例
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
}
return isRunning
}
可以看到最终是执行了AsyncCall的excuteOn方法,excutorService中会执行excute方法,该方法的入参是AsyncCall实例,实际会执行AsyncCall的run方法,在run方法中你会发现有一个老熟人okhttp3.internal.connection.RealCall#getResponseWithInterceptorChain方法,至此就进入了OkHttp的拦截器模式主流程了,其中run方法中会调用okhttp3.Dispatcher#finished方法,该方法会调用promoteAndExecute(又是老熟人了)下一轮的任务调度。
2.3 OkHttp的Socket连接细节
其实整个主流程和拦截器的代码分析中,其实没有涉及到底层的Socket是如何建立的,只能说OkHttp的使用太简便了,简单到我们只需要关心高层的接口使用,底层的Socket建立和池化过程我们在使用OkHttp的时候根本感知不到。接下来我们一起看看被隐藏的Socket细节。
在开始讲Socket之前,回到讲RetryAndFollowUpInterceptor拦截器的intercept方法时,注释了一个很重要的方法RealCall#enterNetworkInterceptorExchange,我们先看下这个方法的实现:
kotlin
fun enterNetworkInterceptorExchange(
request: Request,
newRoutePlanner: Boolean,
chain: RealInterceptorChain,
) {
// a、去掉了部分无关紧要的检查代码
...
if (newRoutePlanner) {
val routePlanner =
RealRoutePlanner(
taskRunner = client.taskRunner,
connectionPool = connectionPool,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis,
socketConnectTimeoutMillis = chain.connectTimeoutMillis,
socketReadTimeoutMillis = chain.readTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
retryOnConnectionFailure = client.retryOnConnectionFailure,
fastFallback = client.fastFallback,
address = client.address(request.url),
connectionUser = CallConnectionUser(this, connectionPool.connectionListener, chain),
routeDatabase = client.routeDatabase,
)
// b、方法的关键,初始化了RealCall的exchangeFinder变量值
this.exchangeFinder =
when {
client.fastFallback -> FastFallbackExchangeFinder(routePlanner, client.taskRunner)
else -> SequentialExchangeFinder(routePlanner)
}
}
}
从上面的代码看真正关键的地方在注释b处,初始化了RealCall的exchangeFinder变量,这个变量有两种取值,默认是FastFallbackExchangeFinder,但是如果设置了OkHttpClient的fastFallback变量为false,则RealCall的exchangeFinder变量会被设置为SequentialExchangeFinder。FastFallbackExchangeFinder和SequentialExchangeFinder都实现了find接口,RealCall的exchangeFinder变量真正被使用到的地方在ConnectInterceptor拦截器中,该方法调用了RealCall的initExchange方法,话不多说直接看代码:
kotlin
internal fun initExchange(chain: RealInterceptorChain): Exchange {
withLock {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
// a、关键的地方
val connection = exchangeFinder.find()
val codec = connection.newCodec(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
withLock {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
可以看到前面RealCall#enterNetworkInterceptorExchange中设置的exchangeFinder变量调用了find方法,这里以FastFallbackExchangeFinder的fin方法为例:
kotlin
override fun find(): RealConnection {
var firstException: IOException? = null
try {
// a、需要连接的Plan不为空或者存在下一个后续Plan,循环处理
while (tcpConnectsInFlight.isNotEmpty() || routePlanner.hasNext()) {
if (routePlanner.isCanceled()) throw IOException("Canceled")
// Launch a new connection if we're ready to.
val now = taskRunner.backend.nanoTime()
var awaitTimeoutNanos = nextTcpConnectAtNanos - now
var connectResult: ConnectResult? = null
if (tcpConnectsInFlight.isEmpty() || awaitTimeoutNanos <= 0) {
// b、发起tcp连接
connectResult = launchTcpConnect()
nextTcpConnectAtNanos = now + connectDelayNanos
awaitTimeoutNanos = connectDelayNanos
}
// Wait for an in-flight connect to complete or fail.
// c、阻塞等待tcp链接
if (connectResult == null) {
connectResult = awaitTcpConnect(awaitTimeoutNanos, TimeUnit.NANOSECONDS) ?: continue
}
// d、tcp连接返回结果
if (connectResult.isSuccess) {
// We have a connected TCP connection. Cancel and defer the racing connects that all lost.
cancelInFlightConnects()
// Finish connecting. We won't have to if the winner is from the connection pool.
if (!connectResult.plan.isReady) {
// e、建立tls连接(最终的http的代理的证书校验、握手在这里)
connectResult = connectResult.plan.connectTlsEtc()
}
if (connectResult.isSuccess) {
// f、链接池处理(简单来说就是把结果存起来,下次好找到)
return connectResult.plan.handleSuccess()
}
}
val throwable = connectResult.throwable
if (throwable != null) {
if (throwable !is IOException) throw throwable
if (firstException == null) {
firstException = throwable
} else {
firstException.addSuppressed(throwable)
}
}
val nextPlan = connectResult.nextPlan
if (nextPlan != null) {
// Try this plan's successor before deferred plans because it won the race!
routePlanner.deferredPlans.addFirst(nextPlan)
}
}
} finally {
cancelInFlightConnects()
}
throw firstException!!
}
代码看起来很多,我在关键的位置都做了注释,其中launchTcpConnect方法调用链路为okhttp3.internal.connection.ConnectPlan#connectTcp>>okhttp3.internal.connection.ConnectPlan#connectSocket>>javax.net.SocketFactory#createSocket,至此socket链接生成。然后看注释e处,调用的链路为okhttp3.internal.connection.ConnectPlan#connectTlsEtc>>javax.net.ssl.SSLSocketFactory#createSocket>>okhttp3.internal.connection.ConnectPlan#connectTls,在connetctTls中完成SslSocket的握手和证书校验,至此完成了Socket建立,通过注释f处的handleSuccess>>okhttp3.internal.connection.RealConnectionPool#put链路,完成连接的池化操作,具体的池化操作可以看RealConnectionPool中的操作。FastFallbackExchangeFinder的find分析结束了,SequentialExchangeFinder实例的socket建立过程与FastFallbackExchangeFinder类似,这里就不再重复分析了。
3.小结
OkHttp这个网络请求框架的代码实现还是非常优雅的,从Call任务的调度实现到数据的缓存、Socket连接池化过程以及串通整个请求流程的拦截器模式等,有非常多值得我们学习的地方。源码的学习本身还是很枯燥的,相信你读完这篇文章也能有一些自己的思考和感悟。加油Android开发人,每天进步一点!!!