OKHttp理解
okhttp是square公司貢獻(xiàn)的一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開(kāi)源框架越锈,是目前Android開(kāi)發(fā)使用最廣泛的一個(gè)網(wǎng)絡(luò)框架烤咧,從Android4.4開(kāi)始酬荞,httpURLconnection的底層實(shí)現(xiàn)采用的就是okhttp。內(nèi)部實(shí)現(xiàn)就是利用java基礎(chǔ)塑荒,對(duì)socket進(jìn)行封裝忽妒,實(shí)現(xiàn)http通信玩裙。最重要的兩個(gè)關(guān)鍵點(diǎn)就是分發(fā)器和5個(gè)攔截器。
分發(fā)器就是內(nèi)部維護(hù)隊(duì)列和線程池段直,完成請(qǐng)求分配吃溅,總結(jié)就是用于對(duì)異步任務(wù)加入隊(duì)列管理,然后判斷條件鸯檬,控制數(shù)量决侈,加入線程池執(zhí)行異步請(qǐng)求任務(wù)。
五個(gè)默認(rèn)攔截器就是利用責(zé)任鏈模式對(duì)網(wǎng)絡(luò)請(qǐng)求進(jìn)行層層處理喧务,完成整個(gè)請(qǐng)求過(guò)程颜及,簡(jiǎn)單總結(jié)如下。
1.橋接攔截器對(duì)用戶發(fā)出的請(qǐng)求添加缺少的請(qǐng)求配置字段蹂楣,比如keep-alive等
2.緩存攔截器就是查詢有沒(méi)有符合判斷條件的已緩存的網(wǎng)絡(luò)請(qǐng)求,執(zhí)行復(fù)用讯蒲,直接返回response
3.連接攔截器就是創(chuàng)建請(qǐng)求痊土,加入連接器 或者訪問(wèn)連接池,根據(jù)條件判斷墨林,是否能懟已創(chuàng)建的tcp請(qǐng)求進(jìn)行復(fù)用
4.請(qǐng)求服務(wù)器攔截器就是對(duì)scoket進(jìn)行操作赁酝,請(qǐng)求網(wǎng)絡(luò)訪問(wèn)服務(wù)器犯祠,返回response,
5.重試和重定向攔截器就是對(duì)返回的response進(jìn)行code判斷酌呆,決定是否要重試或者重定向操作衡载。
okhttp的優(yōu)點(diǎn)
1.支持http2.0版本,并且允許對(duì)同一主機(jī)的所有請(qǐng)求共享一個(gè)套接字
2.即使不是http2.0版本隙袁,通過(guò)連接池痰娱,減少請(qǐng)求延遲
3.默認(rèn)使用Gzip 壓縮數(shù)據(jù)
4.響應(yīng)緩存,避免重復(fù)請(qǐng)求網(wǎng)絡(luò)
okHttp的執(zhí)行流程
最簡(jiǎn)單的http請(qǐng)求案例
val client = OkHttpClient.Builder().build()
val request= Request.Builder().url("https://www.baidu.com").build()
//異步請(qǐng)求
client.newCall(request).enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
//同步請(qǐng)求
client.newCall(request).execute();
1.利用建造者模式構(gòu)建okHttpClient實(shí)例對(duì)象,構(gòu)建過(guò)程中可以動(dòng)態(tài)配置參數(shù)菩收,請(qǐng)求時(shí)間梨睁,響應(yīng)時(shí)間,緩存信息等娜饵。
2.創(chuàng)建Request對(duì)象坡贺,設(shè)置請(qǐng)求方式,鏈接地址箱舞,參數(shù)等信息遍坟。
3.把request對(duì)象,傳給client晴股,通過(guò)newCall函數(shù)愿伴,得到RealCall對(duì)象。
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
4.RealCall 分為同步和異步執(zhí)行
5.同步執(zhí)行時(shí)队魏,分發(fā)器只是做個(gè)記錄公般,把請(qǐng)求任務(wù)加到隊(duì)列中,然后直接通過(guò)攔截器訪問(wèn)服務(wù)器胡桨,返回response官帘。
override fun execute(): Response {
....
try {
client.dispatcher.executed(this)//分發(fā)器記錄任務(wù) runningSyncCalls.add(call)
return getResponseWithInterceptorChain() //返回請(qǐng)求結(jié)果 response
} finally {
client.dispatcher.finished(this)
}
}
6.異步執(zhí)行
6.1先對(duì)異步任務(wù)進(jìn)一步封裝,把任務(wù)放到AsyncCall對(duì)象中
override fun enqueue(responseCallback: Callback) {
...
client.dispatcher.enqueue(AsyncCall(responseCallback))
//AsyncCall(responseCallback)對(duì)異步任務(wù)進(jìn)一步封裝
}
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable
2.分發(fā)器 把 封裝后的異步任務(wù) 添加到等待運(yùn)行的隊(duì)列中
======分發(fā)器維護(hù)的三個(gè)隊(duì)列======
//等待運(yùn)行的異步任務(wù)隊(duì)列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//正在運(yùn)行的異步任務(wù)隊(duì)列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//同步任務(wù)隊(duì)列
private val runningSyncCalls = ArrayDeque<RealCall>()
//異步任務(wù)昧谊,加入隊(duì)列
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
if (!call.call.forWebSocket) {
//在等待隊(duì)列或者運(yùn)行隊(duì)列中找出有沒(méi)有相同host的請(qǐng)求任務(wù)
val existingCall = findExistingCallWithHost(call.host)
//如果有相同host的請(qǐng)求刽虹,那么就把他們的callsPerHost 設(shè)置為同一個(gè)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
//執(zhí)行分發(fā)操作
promoteAndExecute()
}
=======syncCall 字段說(shuō)明==================
//這個(gè)字段用來(lái)設(shè)置 當(dāng)前 正在執(zhí)行的任務(wù),有多少個(gè)是相同host
@Volatile var callsPerHost = AtomicInteger(0)
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
========end=======================
//分發(fā)操作
private fun promoteAndExecute(): Boolean {
//臨時(shí)容器
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
//遍歷等待運(yùn)行的異步任務(wù)隊(duì)列
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
//正在執(zhí)行的異步任務(wù)數(shù)量 不能大于默認(rèn)最大數(shù)量64
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
// 同一個(gè)時(shí)刻呢诬,對(duì)同一個(gè)host的訪問(wèn)數(shù)量涌哲,不能大于5個(gè)
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
//刪除等待隊(duì)列的異步任務(wù)
i.remove()
//設(shè)置任務(wù)主機(jī)Host 的數(shù)量
asyncCall.callsPerHost.incrementAndGet()
//把任務(wù)加入臨時(shí)容器
executableCalls.add(asyncCall)
//把任務(wù)加入正在運(yùn)行的隊(duì)列
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//利用線程池,執(zhí)行異步任務(wù)
asyncCall.executeOn(executorService)
}
return isRunning
}
====線程池的配置======
okHttp 配置的線程池尚镰,等同于jdk提供的newCacheThreadPool
核心線程的數(shù)量為0阀圾,最大線程數(shù)量不限制,阻塞隊(duì)列不能存儲(chǔ)元素狗唉。
這樣配置的好處就是可以并發(fā)執(zhí)行多個(gè)異步任務(wù)初烘。如果阻塞隊(duì)列能添加元素,比如LinkedBlockingQueue 只有等上個(gè)任務(wù)結(jié)束之后,才能執(zhí)行下個(gè)任務(wù)肾筐。
val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
=======end=======
//執(zhí)行異步任務(wù)
fun executeOn(executorService: ExecutorService) {
....
try {
executorService.execute(this) //執(zhí)行syncCall的run函數(shù)
success = true
} catch (e: RejectedExecutionException) {
.....
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
//執(zhí)行子線程任務(wù)
override fun run() {
......
try {
//利用攔截器哆料,請(qǐng)求網(wǎng)絡(luò),分發(fā)器任務(wù)結(jié)束
val response = getResponseWithInterceptorChain()
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
......
} finally {
client.dispatcher.finished(this)
}
}
}
=====同步或者異步吗铐,最終都會(huì)調(diào)用 client.dispatcher.finished(this)======
internal fun finished(call: RealCall) {//同步任務(wù)結(jié)束
finished(runningSyncCalls, call)
}
internal fun finished(call: AsyncCall) {//異步任務(wù)結(jié)束
call.callsPerHost.decrementAndGet() //對(duì)相同主機(jī)數(shù)量 進(jìn)行-1操作
finished(runningAsyncCalls, call)
}
private fun <T> finished(calls: Deque<T>, call: T) {
....
val isRunning = promoteAndExecute() //再次去分發(fā)異步任務(wù)
....
}
=======end======
7.getResponseWithInterceptorChain 通過(guò)攔截器东亦,獲取response
okhttp 默認(rèn)提供5個(gè)攔截器 重試重定向攔截器,橋接攔截器唬渗,緩存攔截器典阵,連接攔截器,訪問(wèn)服務(wù)器攔截器谣妻。還可以自定義攔截器萄喳。
自定義攔截器分為應(yīng)用攔截器(通過(guò)addInterceptor 添加)和網(wǎng)絡(luò)攔截器(通過(guò)addNetworkInterceptor攔截)
=====添加自定義攔截器=========
val client = OkHttpClient.Builder()
.addInterceptor (object :Interceptor{ //添加應(yīng)用攔截器
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val newRequest = request.newBuilder().addHeader("token","11111111").build()
return chain.proceed(newRequest)
}
})
.addNetworkInterceptor(object :Interceptor{//添加網(wǎng)絡(luò)攔截器
override fun intercept(chain: Interceptor.Chain): Response {
省略一些攔截操作 xxxxxxxx
return chain.proceed(chain.request())
}
})
.build()
=========end========
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors //添加應(yīng)用攔截器
interceptors += RetryAndFollowUpInterceptor(client) //重試重定向
interceptors += BridgeInterceptor(client.cookieJar) //橋接
interceptors += CacheInterceptor(client.cache) //緩存攔截器
interceptors += ConnectInterceptor //連接攔截器
if (!forWebSocket) {
interceptors += client.networkInterceptors //網(wǎng)絡(luò)攔截器
}
interceptors += CallServerInterceptor(forWebSocket) //訪問(wèn)服務(wù)攔截器
//構(gòu)建 鏈接對(duì)象
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 {
//經(jīng)過(guò)攔截器 響應(yīng)請(qǐng)求,返回response
val response = chain.proceed(originalRequest)
....
return response
} catch (e: IOException) {
......
}
}
okhttp 攔截器原理解析
攔截器采用責(zé)任鏈的設(shè)計(jì)默認(rèn)蹋半,讓請(qǐng)求者和處理者解耦他巨,最終請(qǐng)求從前往后,響應(yīng)從后往前减江。
重試重定向攔截器 RetryAndFollowUpInterceptor
首先先判斷用戶是否取消了請(qǐng)求染突,如果沒(méi)有取消,就把請(qǐng)求交個(gè)橋接攔截器辈灼。
在獲得響應(yīng)結(jié)果response的時(shí)候根據(jù)響應(yīng)碼份企,判斷是否需要重試或者重定向,重試不限制次數(shù)巡莹,重定向最多20次司志,如果需要重試或者重定向,那么會(huì)再一次重新執(zhí)行所有攔截器降宅。
有如下幾種情況不會(huì)重試:IO異常骂远,線路異常,配置client實(shí)例時(shí)配置不允許重試腰根,協(xié)議異常激才,證書(shū)異常等等。
判斷是否允許重定向使用返回的response的code碼決定的额嘿,或者在構(gòu)建client是設(shè)置是否允許重定向 followRegirects(默認(rèn)為true)
橋接攔截器 BridgeInterceptor
先獲取用戶發(fā)送的請(qǐng)求瘸恼,判斷條件用戶是否已經(jīng)配置過(guò)請(qǐng)求頭字段,若用戶沒(méi)有配置册养,則將http協(xié)議必備的請(qǐng)求頭字段補(bǔ)齊东帅,比如Content-Type,Content-Length等球拦,然后交給下一個(gè)攔截器靠闭。
在獲得響應(yīng)結(jié)果response之后邓夕,調(diào)用保存cookie的接口(也可以在配置client的時(shí)候,設(shè)置cookjar進(jìn)行cookie回調(diào)數(shù)據(jù))阎毅,并且解析gzip數(shù)據(jù)
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
獲取結(jié)果之后,對(duì)cookie進(jìn)行保存点弯,對(duì)返回的數(shù)據(jù)進(jìn)行g(shù)zip解壓
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
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())
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()))
}
緩存攔截器CacheInterceptor
就是根據(jù)緩存策略從緩存中查找是否有合適的緩存response扇调,如果有合適的緩存,直接返回給請(qǐng)求任務(wù)抢肛,不在繼續(xù)執(zhí)行后面的攔截器狼钮。
獲得響應(yīng)結(jié)果response后,根據(jù)條件判斷捡絮,決定是否要緩存熬芜。
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
連接攔截器ConnectInterceptor
維護(hù)一個(gè)連接池,負(fù)責(zé)對(duì)連接的服務(wù)福稳。在把請(qǐng)求交給下一個(gè)攔截器之前涎拉。會(huì)先在連接池中找到一個(gè)合適的連接(滿足適配條件相同,并且沒(méi)有正在被使用)或者新建一個(gè)連接的圆,并且接入連接池鼓拧,獲得對(duì)應(yīng)的socket流,把請(qǐng)求交給下一個(gè)攔截器越妈。獲得response結(jié)果后不會(huì)進(jìn)行額外的處理季俩。
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)
}
}
internal fun initExchange(chain: RealInterceptorChain): Exchange {
....
val exchangeFinder = this.exchangeFinder!!
val codec = exchangeFinder.find(client, chain) //查找連接池
val result = Exchange(this, eventListener, exchangeFinder, codec)
....
}
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
val resultConnection = findHealthyConnection(
....
)
return resultConnection.newCodec(client, chain)
} ...
}
private fun findHealthyConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
doExtensiveHealthChecks: Boolean
): RealConnection {
while (true) {
val candidate = findConnection(... )//攜帶信息,查找連接池.
.....
}
======RealConnection 判斷復(fù)用條件========================
internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
assertThreadHoldsLock()
// 判斷這個(gè)連接是否正在被使用
if (calls.size >= allocationLimit || noNewExchanges) return false
//判斷基礎(chǔ)信息是否相同 證書(shū)梅掠,協(xié)議酌住,url,ssl等
if (!this.route.address.equalsNonHost(address)) return false
// 請(qǐng)的host也必須一直
if (address.url.host == this.route().address.url.host) {
return true // This connection is a perfect match.
}
===必須跟下面的信息相同的請(qǐng)求才能復(fù)用 this.route.address.equalsNonHost(address)=================
internal fun equalsNonHost(that: Address): Boolean {
return this.dns == that.dns &&
this.proxyAuthenticator == that.proxyAuthenticator &&
this.protocols == that.protocols &&
this.connectionSpecs == that.connectionSpecs &&
this.proxySelector == that.proxySelector &&
this.proxy == that.proxy &&
this.sslSocketFactory == that.sslSocketFactory &&
this.hostnameVerifier == that.hostnameVerifier &&
this.certificatePinner == that.certificatePinner &&
this.url.port == that.url.port
}
連接池阎抒,也稱之為對(duì)象池酪我,主要用來(lái)存放request請(qǐng)求連接,內(nèi)部維護(hù)了一個(gè)LinkedQueue隊(duì)列用來(lái)存放請(qǐng)求挠蛉。在添加新的請(qǐng)求對(duì)象時(shí)祭示,都會(huì)執(zhí)行一個(gè)周期性任務(wù),用以對(duì)連接池進(jìn)行清理操作谴古。
1.隊(duì)列長(zhǎng)度超過(guò)5质涛,清理最近未被使用連接,LRE算法
2.存儲(chǔ)的連接掰担,5分鐘未被復(fù)用汇陆,清理
====RealConnectionPool 實(shí)例化====
class ConnectionPool internal constructor(
internal val delegate: RealConnectionPool
) {
constructor(
maxIdleConnections: Int,
keepAliveDuration: Long,
timeUnit: TimeUnit
) : this(RealConnectionPool(//this(5, 5, TimeUnit.MINUTES)
taskRunner = TaskRunner.INSTANCE,
maxIdleConnections = maxIdleConnections,
keepAliveDuration = keepAliveDuration,
timeUnit = timeUnit
))
constructor() : this(5, 5, TimeUnit.MINUTES)
======end===========
class RealConnectionPool(
taskRunner: TaskRunner,
/** The maximum number of idle connections for each address. */
private val maxIdleConnections: Int, //隊(duì)列的長(zhǎng)度-- 5
keepAliveDuration: Long, //休閑請(qǐng)求的存活時(shí)間 ---5
timeUnit: TimeUnit//單位 TimeUnit.MINUTES
) {
//周期性的清理任務(wù)
private val cleanupTask = object : Task("$okHttpName ConnectionPool") {
override fun runOnce() = cleanup(System.nanoTime())
1.隊(duì)列長(zhǎng)度超過(guò)5,清理最近未被使用request LRE算法
2.空閑請(qǐng)求带饱,5分鐘未被復(fù)用毡代,清理
}
//隊(duì)列保存請(qǐng)求對(duì)象
private val connections = ConcurrentLinkedQueue<RealConnection>()
//保存請(qǐng)求對(duì)象阅羹,開(kāi)啟周期性任務(wù)
fun put(connection: RealConnection) {
connection.assertThreadHoldsLock()
connections.add(connection)
cleanupQueue.schedule(cleanupTask)
}
訪問(wèn)服務(wù)攔截器CallServerInterceptor
拿到上一個(gè)攔截器返回的請(qǐng)求,真正的與服務(wù)器進(jìn)行通信教寂,向服務(wù)器發(fā)送數(shù)據(jù)捏鱼,解析讀取響應(yīng)的數(shù)據(jù),返回給上一個(gè)攔截器酪耕。
okhttp 通信流程總結(jié)
okhttp 面試題總結(jié)
OkHttp 請(qǐng)求的整體流程
1.創(chuàng)建request =>OkHttpClient=>RealCall()
2.同步執(zhí)行 导梆,分發(fā)器添加同步任務(wù),執(zhí)行攔截器迂烁,訪問(wèn)服務(wù)器看尼,返回reponse,觸發(fā)異步分發(fā)流程盟步。
3.異步執(zhí)行 藏斩,封裝任務(wù)= >AsyncCall ,實(shí)現(xiàn)runnable接口却盘。添加任務(wù)到異步任務(wù)等待隊(duì)列狰域,執(zhí)行分發(fā)任務(wù),判斷異步任務(wù)是否能加入正在執(zhí)行的異步任務(wù)隊(duì)列谷炸,滿足兩個(gè)條件
同時(shí)執(zhí)行的異步任務(wù)數(shù)量不得大于64個(gè)
對(duì)同一個(gè)主機(jī)的訪問(wèn)任務(wù)北专,最多不得大于5個(gè)
4.加入正在執(zhí)行的異步任務(wù)隊(duì)列,通過(guò)線程池執(zhí)行任務(wù)旬陡,經(jīng)過(guò)5個(gè)默認(rèn)攔截器訪問(wèn)服務(wù)器拓颓,返回response,執(zhí)行異步任務(wù)分發(fā)描孟。
分發(fā)器是如何工作的
分發(fā)器工作 分為同步任務(wù)和異步任務(wù)兩種
同步任務(wù) 就是把任務(wù)加入同步任務(wù)隊(duì)列驶睦,加個(gè)標(biāo)記,執(zhí)行結(jié)束之后匿醒,觸發(fā)異步任務(wù)的分發(fā)操作场航。
異步任務(wù) 先封裝任務(wù)到asyncCall對(duì)象,實(shí)現(xiàn)了runnable接口廉羔。把任務(wù)加入等待執(zhí)行隊(duì)列溉痢,執(zhí)行分發(fā)操作。
先遍歷等待任務(wù)隊(duì)列憋他,判斷是否符合加入正在運(yùn)行的異步任務(wù)隊(duì)列孩饼,要同時(shí)滿足兩個(gè)條件。
同時(shí)執(zhí)行的異步任務(wù)數(shù)量不得大于64個(gè)
對(duì)同一個(gè)主機(jī)的訪問(wèn)任務(wù)竹挡,最多不得大于5個(gè)
當(dāng)滿足條件后镀娶,從等待隊(duì)列中刪除任務(wù),把任務(wù)加入正在執(zhí)行的隊(duì)列中揪罕,通過(guò)自定義的線程池梯码,執(zhí)行任務(wù)宝泵,任務(wù)執(zhí)行結(jié)束后,再次執(zhí)行分發(fā)操作轩娶。
攔截器是如何工作的
攔截器采用了責(zé)任鏈設(shè)計(jì)默認(rèn)儿奶,讓請(qǐng)求者和執(zhí)行者解耦,請(qǐng)求者只需要將請(qǐng)求發(fā)給責(zé)任鏈即可鳄抒,無(wú)需關(guān)心請(qǐng)求過(guò)程和細(xì)節(jié)廓握。okHttp 默認(rèn)有5個(gè)攔截器,重試重定向攔截器嘁酿,橋接攔截器,緩存攔截器男应,連接攔截器闹司,請(qǐng)求服務(wù)攔截器。工作細(xì)節(jié)參考上面攔截器原理分析部分
應(yīng)用攔截器和網(wǎng)絡(luò)攔截器的區(qū)別
1.位置的關(guān)系沐飘,應(yīng)用攔截器 放在責(zé)任鏈最頂端游桩,網(wǎng)絡(luò)攔截器放在責(zé)任鏈倒數(shù)第二的位置。所以應(yīng)用攔截器 最先攔截耐朴,最后響應(yīng)借卧,網(wǎng)絡(luò)攔截器 倒數(shù)第二攔截,第二響應(yīng)筛峭。如果打印請(qǐng)求日志的情況铐刘,應(yīng)用攔截器打印的是用戶請(qǐng)求信息,經(jīng)過(guò)重試重定向影晓,橋接镰吵,緩存,鏈接 等攔截器的層層包裝挂签,網(wǎng)絡(luò)攔截器打印的是實(shí)際請(qǐng)求的信息疤祭。
2.應(yīng)用攔截器一定會(huì)被執(zhí)行,網(wǎng)絡(luò)攔截器不一定被執(zhí)行饵婆。
okHttp 如何復(fù)用TCP連接
利用連接池勺馆,緩存所有的有效連接對(duì)象。
清理機(jī)制:垃圾連接
1.超過(guò)5分鐘沒(méi)有用過(guò)的鏈接
2.超過(guò)5個(gè)閑置鏈接后侨核,從最久閑置的鏈接開(kāi)始執(zhí)行清理(LRU)