问:简述OkHttp简单使用流程(基本不会这么问,为了分析)
答:OkHttp使用流程基本分为四个步骤。
1、创建OkHttpClient实例
2、创建Request
3、OkHttpClient生成一个Call对象实例(RealCall)
4、发送请求(同步、异步)
如:
//1、创建一个OkHttpClient 的实例
val client = OkHttpClient.Builder().build()
//2、创建一个request请求体
val request = Request.Builder().url("http://www.baidu.com").get().build()
//3、生成真实请求对象
val newCall = client.newCall(request)
//4、发送同步请求,请求同步的,需要在子线程中执行
val response = newCall.execute()
//发送异步请求,请求异步,请求结果自动Callback回调。
newCall.enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
//请求失败(正常来讲是网络之类的问题,无法请求到服务端,否则服务端都有会response,不管是200还是302还是500之类的)
}
override fun onResponse(call: Call, response: Response) {
//请求成功
}
})
1、创建OkHttpClient实例:
OkHttp中使用 建造者模式(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。)来设计OkHttpClient,里面包含了如:调度器(Dispatcher)、连接池(ConnectionPool)、拦截器(Interceptor)等等,具体看一下源码,无需关注太多,后面会挑重点解析。
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher() //调度器(用于分发)
internal var connectionPool: ConnectionPool = ConnectionPool() //连接池
internal val interceptors: MutableList<Interceptor> = mutableListOf() //拦截器list
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf() //网络拦截器list
internal var retryOnConnectionFailure = true //连接失败重试
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES //cookies
internal var cache: Cache? = null //缓存
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS //支持的协议列表
internal var connectTimeout = 10_000 //连接超时时间默认10s
internal var readTimeout = 10_000 //读取超时时间默认10s
internal var writeTimeout = 10_000 //写入超时时间默认10s
//...中间及后面省略一堆代码
}
2、创建一个request请求体:
也是使用建造者模式来设计Request,里面包含了:请求url(url)、请求方式(method)、header头(headers)、请求体(body),如:
open class Builder {
internal var url: HttpUrl? = null //请求url
internal var method: String //请求方式
internal var headers: Headers.Builder //header头
internal var body: RequestBody? = null //请求体
//...省略一堆代码
}
3、OkHttpClient生成一个Call对象实例(RealCall)
client调用newCall方法,获得一个Call对象(Call是一个接口,具体的实现是RealCall类)。
//OkHttpClient.kt
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
//其中,this就是OkHttpClient的对象实例,request就是第二步生成的request对象,默认websocket为false
//RealCall
class RealCall( val client: OkHttpClient, val originalRequest: Request, val forWebSocket: Boolean) : Call {
private val connectionPool: RealConnectionPool = client.connectionPool.delegate
//省略一堆代码,其实就是通过client获取Client中的参数设置
}
4、发送请求(同步、异步):
通过第三步获取的call对象,调用其实现类RealCall的 同步方法execute() 或 异步方法enqueue(callback)。
接下来可能就问了:
问:OkHttpClient有几种发起请求的方式,有何不同?
答: 有两种,同步请求和异步请求。同步请求将请求(任务)加入到调度器(Dispatcher)的runningSyncCalls(正在执行)双端队列中,然后直接调用了getResponseWithInterceptorChain,而异步请求将请求加入到调度器(Dispatcher)中,经历两个阶段:readyAsyncCalls、runningAsyncCalls,之后调用
getResponseWithInterceptorChain。
//同步:RealCall.kt
override fun execute(): Response {
//检查任务是否执行过
check(executed.compareAndSet(false, true)) { "Already Executed" }
//开始超时计时
timeout.enter()
//加入事件监听
callStart()
try {
//加入到正在运行同步双端队列中
client.dispatcher.executed(this)
//调用getResponseWithInterceptorChain方法获取Response
return getResponseWithInterceptorChain()
} finally {
//无论是否成功,都将任务从双端队列中移除
client.dispatcher.finished(this)
}
}
//异步:RealCall.kt
override fun enqueue(responseCallback: Callback) {
//检查任务是否执行过
check(executed.compareAndSet(false, true)) { "Already Executed" }
//加入事件监听
callStart()
//将任务加入到调度器中,调度器中将任务添加到readyAsyncCalls 准备执行的双端队列中
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
//Dispatcher.kt
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
//加入到准备执行的双端队列中
readyAsyncCalls.add(call)
// 更改AsyncCall,以使其共享到同一主机的现有运行调用的AtomicInteger,Dispathcer中定义了同一主机同时执行的最大并发数为5个
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
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 // 这里会判断正在执行的队列是否大于最大的数量 这里定义的最大数为64
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // 同主机最大并行数 5
//从准备执行队列中移除,使用迭代器不影响继续遍历。
i.remove()
//将要执行的任务的主机记录
asyncCall.callsPerHost.incrementAndGet()
//加入到要执行的队列中,后面遍历执行
executableCalls.add(asyncCall)
//加入到正在执行的队列中
runningAsyncCalls.add(asyncCall)
}
//正在执行数是否大于0,没有执行的情况下,就空闲下来。
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//executorService是一个核心线程池为0的无界线程池,且60秒空闲线程回收
asyncCall.executeOn(executorService)
}
return isRunning
}
//先看一下ExecutorService线程池:
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
//核心线程数为0,无界线程池,60秒空闲回收,并指定了线程名称
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
//接着执行asyncCall.executeOn()方法:
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//重点在这里,excute接收一个Runable对象,之后会执行run方法。
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
//如果不是异常,则不管请求成功与否都会从执行队列中清除。
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
//到这里就执行到了Runable对象的run方法:
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
//开始超时计时
timeout.enter()
try {
//调用getResponseWithInterceptorChain方法获取Response
val response = getResponseWithInterceptorChain()
signalledCallback = true
//回调结果
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
//这个方法会重新调用promoteAndExecute方法,这样就又开始了异步的下一轮的请求了。
client.dispatcher.finished(this)
}
}
}
}
源码一堆,不给个总结太对不起大家了(上面源码都有注释,也可以看看注释):
1、异步和同步最后都会执行getResponseWithInterceptorChain()方法获取任务执行结果,这里说任务其实是因为这里暂时还没开始去进行网络请求,只能说是一个任务
2、异步执行这个任务时,只是很简单将任务加入到正在执行的同步队列runningSyncCalls中,而异步执行经历了两个队列:readyAsyncCalls、runningAsyncCalls
3、任务执行完成后,不管异步还是同步,都会将加入到队列中的任务清除
4、同步执行实际上并没有排队的概念,异步执行是需要进行排队的,并且同主机任务最大支持5个并发,同时执行任务不超过64个
到这里,经常出现的两个词:调度器是什么?线程池又是怎么实现的?
问:OkHttp中的Dispatcher调度器是什么,是怎么实现调度的
答:调度器就是对任务执行的调度,Dispatcher中维护了三个队列,分别是一个准备异步执行队列readyAsyncCalls,两个正在执行的队列(异步正在执行队列:runningAsyncCalls,同步正在执行队列:runningSyncCalls),在每个任务执行完成最后都会执行dispatch.finish()方法,之后会重新执行到promoteAndExecute()方法,意为:促进和执行。只要队列中有任务,就会一直重复执行。 同时,调度器中定义了两个并发执行数的变量,分别对并发做限制,最大同时执行数为64,同主机执行数为5。调度器构造方法中定义了一个 ExecutorService 线程池变量,利用线程池对任务进行执行操作。
问:OkHttp中的线程池是怎么实现的?
答:OkHttp的调度器中初始化了一个线程池,线程池没有核心线程,最大支持线程为Int.MAX_VALUE(无界线程池),同时定义了60秒空闲时间,超过60秒的空线程将会回收。并且通过ThreadFactory生成的线程定义了线程名称。其工作队列为SynchronousQueue同步队列。当一个任务到来时,会加入到同步队列中,如果有空闲线程则直接从同步队列中取出,在空闲线程中进行处理,当没有空闲线程时,则会先创建一个新的线程再接受任务进行执行。OkHttp线程池设计为核心线程为0是因为客户端可能在一段时间内不会有网络请求,为了避免浪费不必要的线程内存,所以不保留最低线程,同时最大线程设置为Int.MAX_VALUE为了防止同一时间有大量的请求进入,造成部分请求被抛弃的问题,设置60秒为线程空闲最大时间,在一段时间不使用的情况进行线程回收。
前面说了,不管同步还是异步,最终都会执行 getResponseWithInterceptorChain()方法获取网络请求结果。
问题又来了:
问:OkHttp有哪些拦截器,作用分别是什么?
答:总共有七个拦截器,五个内置拦截器,两个自己添加的拦截器:
1、addInterceptor(Interceptor),这是由开发者设置的,会按照开发者的要求,在所有的拦截器处理之前进行最早的拦截处理,比如一些公共参数,Header都可以在这里添加。
2、RetryAndFollowUpInterceptor,这里会对连接做一些初始化工作,以及请求失败的重试工作,重定向的后续请求工作。
3、BridgeInterceptor,这里会为用户构建一个能够进行网络访问的请求,同时后续工作将网络请求回来的响应Response转化为用户可用的Response,比如添加文件类型,content-length计算添加,gzip解包。
4、CacheInterceptor,这里主要是处理cache相关处理,会根据OkHttpClient对象的配置以及缓存策略对请求值进行缓存,而且如果本地有了可⽤的Cache,就可以在没有网络交互的情况下就返回缓存结果。
5、ConnectInterceptor,这里主要就是负责建立连接了,会建立TCP连接或者TLS连接,以及负责编码解码的HttpCodec。
6、networkInterceptors,这里也是开发者自己设置的,所以本质上和第一个拦截器差不多,但是由于位置不同,用处也不同。这个位置添加的拦截器可以看到请求和响应的数据了,所以可以做一些网络调试。
7、CallServerInterceptor,这里就是进行网络数据的请求和响应了,也就是实际的网络I/O操作,通过socket读写数据。
看看getResponseWithInterceptorChain()方法的源码
//RealCall.kt
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors //添加自己的拦截器,使用时通过addInterceptor()添加
interceptors += RetryAndFollowUpInterceptor(client) //添加重试跟踪拦截器
interceptors += BridgeInterceptor(client.cookieJar) //添加桥拦截器
interceptors += CacheInterceptor(client.cache) //添加缓存拦截器
interceptors += ConnectInterceptor //添加链接拦截器
if (!forWebSocket) {
//如果不是websocket,添加自己的network拦截器,通过addNetworkInterceptor()添加
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket) //添加呼叫服务器拦截器
//将拦截器封装到 RealInterceptorChain中
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
//...省略代码
try {
//通过RealInterceptorChain实例对象执行(实际是是对每个拦截器的实现调用)
val response = chain.proceed(originalRequest)
//...省略代码
return response
} catch (e: IOException) {
//...省略代码
} finally {
//...省略代码
}
}
getResponseWithInterceptorChain()方法实际上是对几个Interceptors拦截器加到RealInterceptorChain类(拦截器链)中,之后通过proceed方法进行执行(proceed方法意为继续的意思,实际上每个拦截器都会执行这个),我们这里先不管每个拦截器的实现以及原理是什么,我们先看看是怎么执行的。
//RealInterceptorChain.kt
override fun proceed(request: Request): Response {
//省略一堆的判断逻辑...
// 在这个链中调用下一个拦截器
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
//执行拦截器的 intercept方法
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
//...省略一堆的代码逻辑
//每个链都会返回一个response。
return response
}
到这里知道,其实Interceptor是一个接口,实现类就是上面的几个拦截器。这里其实就是使用了 责任链模式,每个拦截器中都会调用proceed方法,也都会返回response,调用流程如下:(偷来的图,里面缺了第一个自己添加的拦截器及自己添加的network拦截器)
看看每个拦截器的intercept,添加的两个自定义拦截器都需要实现Interceptor接口,实现intercept方法,并且最后需要调用proceed方法:chain.proceed()。 如:
//添加一个拦截器
val client = OkHttpClient.Builder().addInterceptor(object : Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
//在自己添加的拦截器中可以添加header头等操作
builder.addHeader("Content-Type", "application/json;charset=UTF-8")
val request = builder.build()
//1、需要调用proceed方法,继续调用后面的拦截器
val response = chain.proceed(request)
val mediaType = response.body!!.contentType()
val content = response.body!!.string()
//2、返回response,责任链模式的真实写照
return response.newBuilder()
.body(ResponseBody.create(mediaType, content))
.build()
}
}).build()
回过头来,看看OkHttp内置的五个拦截器的实现:
一、RetryAndFollowUpInterceptor拦截器:该拦截器从故障中恢复,并根据需要进行重定向
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
while (true) {
try {
try {
//调用链上后面的拦截器,获取结果
response = realChain.proceed(request)
} catch (e: RouteException) {
//路由错误,退出本次循环,进入下次循环
continue
} catch (e: IOException) {
// 请求错误,退出本次循环,进入下次循环
continue
}
//获取响应码判断是否需要重定向
val followUp = followUpRequest(response, exchange)
if (followUp == null) {
//没有重定向
return response
}
if(followUp.body != null){
//没有重定向
return response
}
//赋予重定向请求,再次进入下一次循环
request = followUp
}
}
}
}
1、重试和重定向的处理都是需要重新请求,所以这里用到了while循环。
2、当发生请求过程中错误的时候,也就是通过continue进入下一次循环进行 《重试》 ,重新走到realChain.proceed方法调用后面的链的拦截器,最后再进行网络请求。
3、当请求结果没有重定向,那么就直接返回response响应结果。
4、当请求结果需要重定向的时候,就赋予新的请求,并进入下一次循环,重新请求网络。
二、BridgeInterceptor桥拦截器:从应用程序代码到网络代码的桥梁。首先,它根据用户请求建立一个网络请求,然后,它继续呼叫网络,最后,它从网络响应建立用户响应。
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//添加各类Header头信息,省略了很多判断代码...
requestBuilder.header("Content-Type", contentType.toString())
requestBuilder.header("Host", userRequest.url.toHostHeader())
requestBuilder.header("Connection", "Keep-Alive")
requestBuilder.header("Accept-Encoding", "gzip")
requestBuilder.header("Cookie", cookieHeader(cookies))
requestBuilder.header("User-Agent", userAgent)
//同样的,每个拦截器都会调用后面的拦截器,获取response
val networkResponse = chain.proceed(requestBuilder.build())
//如果我们添加“ Accept-Encoding:gzip”标头字段,我们还将负责解压缩//传输流。
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
//每个拦截器都会返回response
return responseBuilder.build()
}
桥拦截器的作用其实就是给网络请求包装一层header头信息,链接应用程序代码和网络请求代码的一个桥梁。使用gzip(一种压缩方式,OkHttp中使用okio框架中的GzipSource类实现)压缩数据,进行数据传输,提高网络传输效率。
三、CacheInterceptor缓存拦截器:服务于来自缓存的请求,并将响应写入缓存。
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//取缓存
val cacheCandidate = cache?.get(chain.request())
//缓存策略类
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
// 不允许使用网络并且缓存为空,直接返回504(504这里是前端的code)
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)//504
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// 如果不允许使用网络,但是有缓存,直接返回缓存。
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
//调用链后面的拦截器继续网络请求
networkResponse = chain.proceed(networkRequest)
// 如果缓存不为空
if (cacheResponse != null) {
//304,表示数据未修改,直接返回数据,更新缓存
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
cache.update(cacheResponse, response)
return response
}
}
//获取真实网络请求数据
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
//如果开发者设置了缓存,则将响应数据缓存
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
//缓存header
val cacheRequest = cache.put(response)
//缓存body
return cacheWritingResponse(cacheRequest, response)
}
}
return response
}
}
缓存拦截器主要做了以下几个事情:
1、通过缓存策略(CacheStrategy)获取缓存
2、判断是否可以使用网络,如果不允许使用网络时:缓存为空,则直接返回504,缓存不为空直接返回缓存
3、可以使用网络时,调用后续的拦截器继续网络请求,获取网络请求结果
4、请求结果下,如果有缓存比,比对code是否为304,如果是则说明缓存还可以使用,直接返回缓存,且更新缓存
5、如果缓存不可用了,则新增/更新缓存,返回最新网络请求结果。
这里会有几个问题:(1)缓存是怎么存储和获取的?(2)每次请求都会去存储和获取缓存吗?(3)缓存策略(CacheStrategy)到底是怎么处理网络和缓存的?networkRequest什么时候为空?
(1)缓存是怎么存储和获取的?
//获取缓存代码
val cacheCandidate = cache?.get(chain.request())
//具体的方法实现
internal fun get(request: Request): Response? {
//使用Request的url作为缓存的key
val key = key(request.url)
//使用DiskLruCache算法存储缓存
val snapshot: DiskLruCache.Snapshot = try {
cache[key] ?: return null
}
val entry: Entry = try {
Entry(snapshot.getSource(ENTRY_METADATA))
}
val response = entry.response(snapshot)
if (!entry.matches(request, response)) {
response.body?.closeQuietly()
return null
}
return response
}
OkHttp的缓存通过Request的url作为key进行存储,使用DiskLruCache算法对缓存进行存储。
(2)每次请求都会去存储和获取缓存吗?
这里需要看一下cache是否为空?cache变量为空的话不会有缓存,所以这个变量什么时候初始化的?
上图说明,cache应该是在创建OkHttpClient时创建的:
val client = OkHttpClient.Builder().cache(Cache(cacheDir, 10 * 1024 * 1024))
所以第二个问题应该是:当开发者设置了缓存以后才会去存储和获取缓存,并且缓存大小有限制。
(3)缓存策略(CacheStrategy)到底是怎么处理网络和缓存的?networkRequest什么时候为空?
class CacheStrategy internal constructor(
val networkRequest: Request?,
val cacheResponse: Response?
)
fun compute(): CacheStrategy {
val candidate = computeCandidate()
return candidate
}
private fun computeCandidate(): CacheStrategy {
//没有缓存情况下,返回空缓存
if (cacheResponse == null) {
return CacheStrategy(request, null)
}
//...
//缓存控制不是 no-cache,且未过期
if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
val builder = cacheResponse.newBuilder()
return CacheStrategy(null, builder.build())
}
return CacheStrategy(conditionalRequest, cacheResponse)
}
在这个缓存策略生存的过程中,只有一种情况下会返回缓存,也就是缓存控制不是no-cache,并且缓存没过期情况下,就返回缓存,然后设置networkRequest为空。
四、ConnectInterceptor连接拦截器:打开与目标服务器的连接,然后进入下一个拦截器。该网络可能用于返回的响应,或者用于使用条件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)
}
}
连接拦截器的代码是真的简单,其他不说,就调用了 initExchange方法,Exchange是交换的意思,其实就是用网络请求地址打开一个通道,使得前后端能通过这个通道进行数据传递。
initExchange()方法最终执行到:findConnection()方法(返回用于托管新流的连接。如果存在现有连接,它将优先使用,然后是池,最后建立一个新连接。)
private fun findConnection(): RealConnection {
// 1、尝试复用当前连接
val callConnection = call.connection
if (callConnection != null) {
//检查这个连接是否可用和可复用
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
return callConnection
}
//2、从连接池中获取可用连接
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
//3、从连接池中获取可用连接(通过一组路由routes)
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
return result
}
route = localRouteSelection.next()
// 4、创建新连接
val newConnection = RealConnection(connectionPool, route)
newConnection.connect
// 5、再获取一次连接,防止在新建连接过程中有其他竞争连接被创建了
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
return result
}
//6、还是要使用创建的新连接,放入连接池,并返回
connectionPool.put(newConnection)
return newConnection
}
实际上,连接拦截器是获取一个与服务端真实的连接。如果当前有可以复用的就用复用的,没有的话从连接池中取,连接池中也没有则创建一个新的连接,然后再放入连接池,再将这个连接返回。OkHttp中连接池最大可以容纳5个空闲连接,五分钟空闲时间后将回收,具体可与看一下ConnectionPool.kt类:
class ConnectionPool internal constructor(
internal val delegate: RealConnectionPool
) {
constructor(
maxIdleConnections: Int,
keepAliveDuration: Long,
timeUnit: TimeUnit
) : this(RealConnectionPool(
taskRunner = TaskRunner.INSTANCE,
maxIdleConnections = maxIdleConnections,
keepAliveDuration = keepAliveDuration,
timeUnit = timeUnit
))
//连接池最大支持5个空闲连接,最大空闲时间为5分钟
constructor() : this(5, 5, TimeUnit.MINUTES)
//省略...
}
这里就留下一点坑吧,要不然篇幅太长太长了,我也是读源码读到这里发现这个连接池的东西,有时间再详细看看这个连接池的具体设计。
五、CallServerInterceptor拦截器:这是链中的最后一个拦截器,它与服务器进行数据交换
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//写header数据
exchange.writeRequestHeaders(request)
//写body数据
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
exchange.noRequestBody()
}
//结束请求
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
//获取响应数据
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.build()
var code = response.code
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
return response
}
}
实际上,在连接拦截器中,已经拿到连接,打开了与服务器的通道,这里就剩下read和write操作了,最后返回response。
最后:
OkHttp使用建造者模式来创建client以及request,调用newCall方法后获取Call对象,实际上是RealCall对象。之后不管是同步还是异步都会先执行自定义拦截器,五个内置拦截器,其中还会有一个network拦截器,定义在倒数第二个,用来获取最后数据交互的真实数据及响应前的数据。拦截器采用 责任链模式 来一层层顺序调用,顺序返回结果。