文章目錄
本文基於OkHttp版本,該版本是用Kotlin實現:
com.squareup.okhttp3:okhttp:4.7.2
1-基本流程
OkHttp這個框架就不用過多介紹了吧,一句話概括就是對Socket編程的封裝實現,方便實現網絡通信。什麼是Socket編程?Socket是TCP/IP協議的抽象實現,詳情可以去看看:一篇文章帶你熟悉 TCP/IP 協議-(一)
String testUrl = "https://wanandroid.com/wxarticle/chapters/json";
//@1.初始化OkHttpClient
OkHttpClient client = new OkHttpClient();
//@2.根據url構建Request
final Request request = new Request.Builder().url(testUrl).build();
//@3.發起同步請求
Response syncResponse = client.newCall(request).execute();
//@4.發起異步請求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {//TODO 失敗回調 }
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {//TODO 成功回調}
});
@1.第一步當然是初始化工作了,通過Builder模式構建OkHttpClient。主要是初始化一些基本參數配置,包括任務調度器、連接池、攔截器、線程池等。後面會詳細講述這些內容。核心是任務調度器和攔截器,任務調度器Dispatcher主要是維護了三個任務隊列:
- readyAsyncCalls = ArrayDeque()
異步方式待執行的任務隊列 - runningAsyncCalls = ArrayDeque()
異步方式正在執行的任務隊列 - runningSyncCalls = ArrayDeque()
同步方式執行的任務隊列
攔截器機制後面將作爲重點講述。
@2.第二步開始構造請求內容。根據Http協議我們知道發起網絡請求的報文協議是請求行+請求頭+請求體。這一步就是通過Builder模式將http協議報文的必要信息封裝到一個Request對象,後面會將該Request對象轉爲符合http協議的報文發起一個網絡請求。
internal constructor(request: Request) {
this.url = request.url//將字符串url轉換成HttpUrl對象,便於提取Url中的scheme、path等信息
this.method = request.method//請求方法post、get等
this.body = request.body//請求體,post方法需要將內容放在請求體
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
//請求頭,這裏是用數組實現,
//key、value、key、value這樣存儲鍵值對,所以肯定是偶數個
this.headers = request.headers.newBuilder()
}
@3、@4本質上原理是一樣的,只是一個是同步實現一個是異步實現,同步異步不做多解釋,我們就看看同步請求的實現來了解其原理。client.newCall(request).execute()
這裏就是通過傳入request,創建了一個Call的實現類RealCall的對象,並調用其execute方法執行請求任務,執行完畢返回一個Resonse對象。
override fun execute(): Response {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
timeout.enter()
callStart()
try {
//將請求任務添加到dispatcher的runningSyncCalls隊列
client.dispatcher.executed(this)
//@5.通過攔截器鏈一步步處理請求任務
return getResponseWithInterceptorChain()
} finally {
//任務執行完畢從runningSyncCalls隊列移除該任務
client.dispatcher.finished(this)
}
}
@5.通過攔截器鏈一步步處理請求任務。這裏就是OkHttp框架的精髓了,將網絡請求Request通過一個InterceptorChain加工處理生產出請求結果Response。InterceptorChain就像一條流水生產線,鏈式調用、分工明確。直接看代碼吧:
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
//(1)用戶自定義頂層全局攔截器,優先級最高
interceptors += client.interceptors
//(2)錯誤、重定向攔截器
interceptors += RetryAndFollowUpInterceptor(client)
//(3)橋接攔截器,橋接應用層與網絡層,添加必要的頭信息
interceptors += BridgeInterceptor(client.cookieJar)
//(4)緩存處理攔截器
interceptors += CacheInterceptor(client.cache)
//(5)連接攔截器。建立C與S的Socket連接
interceptors += ConnectInterceptor
//(6)用戶自定義的NetworkInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
//(7)訪問服務端攔截器。
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
)
var calledNoMoreExchanges = false
try {
//通過RealInterceptorChain按順序執行攔截器,最終得到Response
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)
}
}
}
先大概瞭解下RealInterceptorChain是如何一步步將Request對象加工成Response對象的,第二章再分析各Interceptor的原理。前面將這些攔截器按順序添加到了interceptors中,所以RealInterceptorChain.proceed方法就是從index=0開始執行對應攔截器的intercept方法,流程如下(第3章流程總結中有總的流程圖):
- (1)用戶自定義的Interceptor(可選)。通過OkHttpClient.Builder().addInterceptor添加,實際應用中會在此流程中往request添加一些全局的業務參數,比如cuid、userId、位置信息等
- (2)RetryAndFollowUpInterceptor。開啓無限循環,執行後續攔截器步驟,根據返回的response
- 不需要重試或重定向或者不能再重試、重定向,直接返回response跳出循環
- 重試或重定向,更新request並再次執行後續步驟
- (3)BridgeInterceptor。
- 請求前,將Request對象轉換爲包含http協議信息的請求
- 執行後續攔截器
- 請求後,將返回結果Gzip解壓出響應體內容
- (4)CacheInterceptor。
- 請求前,判斷緩存策略,強制緩存則直接返回緩存,不再發起網絡請求
- 執行後續攔截器
- 請求後,對比緩存策略則使用緩存,否則使用網絡返回response,更新緩存
- (5)ConnectInterceptor
- 判斷當前連接是否可用,不可用則從連接池中獲取連接
- 獲取失敗則新建連接並執行TCP三次握手建立連接
- 執行後續流程
- (6)NetworkInterceptor(可選)。在建立連接後–>正式發送請求前的過程,用戶進行一些處理。
- (7)CallServerIntercepto
- 請求前,將請求request encode爲http協議的報文
- 請求後,從HTTP響應報文decode出response
2-攔截器
2.1-RetryAndFollowUpInterceptor
經歷了用戶自定義的Interceptor,第一個攔截器就是RetryAndFollowUpInterceptor,該攔截器主要處理重試和重定向,所以其實現是遞歸調用RealInterceptorChain.proceed方法執行後續步驟,根據遞歸結果判斷是否需要重試/重定向。來看代碼:
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
。。。//代碼省略
//開啓循環
while (true) {
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {//請求取消
throw IOException("Canceled")
}
try {
//遞歸調用,執行後續Interceptor步驟
response = realChain.proceed(request)
newExchangeFinder = true
} catch (e: RouteException) {
// @6.連接錯誤,判斷是否可以重試
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
//不能重試,拋出異常
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
//重試
continue
} catch (e: IOException) {
// IO錯誤,判斷是否可以重試
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
//重試
continue
}
// 將上一次response合併,一般上一次回覆爲重定向response,無響應體
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
//根據response的code判斷是否需要重定向
//將response中的重定向信息封裝成重定向Request
val followUp = followUpRequest(response, exchange)
//不需要重定向,直接返回結果
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
return response
}
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
return response
}
//關閉當前請求的響應流
response.body?.closeQuietly()
//判斷是否超過最大重定向次數
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
//繼續循環,執行重定向請求
request = followUp
//緩存當前response
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
@6.recover方法判斷是否可以重試,返回false則不允許重試。
private fun recover(
e: IOException,
call: RealCall,
userRequest: Request,
requestSendStarted: Boolean
): Boolean {
// OkHttpClient設置了不允許重試
if (!client.retryOnConnectionFailure) return false
// StreamedRequestBody類型的請求只能執行一次,不允許重試
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// 協議錯誤、安全問題不允許重試
if (!isRecoverable(e, requestSendStarted)) return false
// 無更多可以路由,不允許重試
if (!call.retryAfterFailure()) return false
return true
}
源碼看下來RetryAndFollowUpInterceptor其實工作比較簡單,就是根據後面攔截器處理的結果來判斷是否需要重試、重定向,需要的話就再次執行後面的鏈路,不需要則跳出循環直接返回結果。
總結下RetryAndFollowUpInterceptor的處理流程:
2.2-BridgeInterceptor
BridgeInterceptor看名字就知道是建立網絡連接的橋樑,請求前將Response對象加工成http協議的request,請求後再解壓返回數據gzip,將數據封裝爲http協議的response。來看代碼:
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
//構建http請求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())
}
。。。//代碼省略,沒什麼好說的,就是構建http請求頭部分的鍵值對
//遞歸調用後面的攔截器,獲取返回的結果
val networkResponse = chain.proceed(requestBuilder.build())
//緩存cookie
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
//構建http響應response
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
//gzip編碼方式
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
//解壓gzip,根據解壓出來的數據構建響應頭和響應體
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的職責也是相當明確,網絡請求前將response對象轉換爲http請求,網絡請求返回後再將結果封裝成http response
2.3-CacheInterceptor
CacheInterceptor主要作用就是處理緩存,同樣也分請求前和請求後兩部分。請求前判斷是否可以直接用緩存,請求後更新緩存。
這裏需要注意OkHttp的cache默認是未實現的,需要在構造OkHttpClient時設置Cache,Cache是通過封裝DiskLruCache來管理緩存文件。
//定義緩存路徑及緩存閾值
Cache cache = new Cache(directory, maxSize);
new OkHttpClient.Builder().cache(cache).build();
再來看看CacheInterceptor的源碼:
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
//根據rquest查找對應的緩存response作爲候選緩存cacheCandidate
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
//@7.根據request和cacheCandidate決策採用哪種緩存策略
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
//緩存策略產生的有效緩存cacheResponse
val cacheResponse = strategy.cacheResponse
//記錄緩存命中情況
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
//有效緩存爲空,關閉流
if (cacheCandidate != null && cacheResponse == null) {
cacheCandidate.body?.closeQuietly()
}
// 無網絡且無有效緩存,組裝並返回504 Response
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)")
.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)
}
}
//緩存命中/miss回調
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
//執行後續攔截器步驟
networkResponse = chain.proceed(networkRequest)
} finally {
// 關閉流避免內存泄漏
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
//對比緩存策略
if (cacheResponse != null) {
//服務端返回304 NotModified表明緩存可用
//緩存封裝並返回
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
cache!!.trackConditionalCacheHit()
//response更新緩存cacheResponse
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
//未使用緩存情況
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
//根據Http響應狀態碼及是否有body判斷是否可以緩存
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
//將networkResponse緩存
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
//根據請求方法判斷是否爲非法緩存
//POST/PATCH/PUT/DELETE/MOVE方法不緩存
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
}
}
}
return response
}
@7.根據request和cacheCandidate決策採用哪種緩存策略。關鍵代碼
CacheStrategy.compute–>CacheStrategy.computeCandidate,代碼內容較長就不貼出來了,這裏會決策出兩種緩存策略:
- 強制緩存,即緩存在有效期就直接返回該緩存,不再進行網絡請求
- 對比緩存,緩存超過有效期,進行網絡請求。若服務端返回304 NotModified表示和上次請求對比數據內容並未修改,則該緩存仍然有效;若服務端正常響應則返回服務端數據。
總結下CacheInterceptor的流程:
2.4-ConnectInterceptor
ConnectInterceptor看名字就知道是處理網絡連接的攔截器,源碼很少,職責就是通過Socket建立連接,優先使用已存在的connection,其次從CoonectionPool中獲取connection並connect,最後都沒有則新建connection並connect
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
//@8.核心,從連接池connectionPool查找可用連接或新建連接
//來傳送request或接收response
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
@8.調用鏈路ConnectionInterceptor.intercept–>RealCall.initExchange–>ExchangeFinder.find
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
//@9.查找可用的連接
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
//將相關連接信息封裝成Http2ExchangeCodec/Http1ExchangeCodec返回
//以便下一個攔截器CallServerInterceptor開始進行通信
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
@9.查找可用連接,通過循環調用findConnection找到連接,優先查找當前連接,其次連接池中的connection,最後新建連接。如果連接可用則跳出循環。看下其核心方法findConnection
//如果該連接不可用,findHealthyConnection會移除該連接
//並重新進入findConnection方法查找
private fun findHealthyConnection(。。。//形參省略): RealConnection {
while (true) {
//@10.查找連接,優先當前連接,其次連接池,最後新建連接
val candidate = findConnection(。。。//實參省略)
// 連接可用則直接返回該連接,跳出循環
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
// 不可用則移除該連接,繼續循環調用findConnection查找
candidate.noNewExchanges()
。。。//代碼省略
}
}
@10.查找連接,優先當前連接,其次連接池,最後新建連接
private fun findConnection(。。。//形參省略): RealConnection {
。。。//省略
synchronized(connectionPool) {
。。。//省略
//如果當前已建立連接,直接返回
if (call.connection != null) {
result = call.connection
releasedConnection = null
}
if (result == null) {
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
//當前連接不可用,從連接池中通過host和port查找連接
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
//連接池中找到緩存,標記
foundPooledConnection = true
result = call.connection
} else if (nextRouteToTry != null) {
//未找到,且還有待查找路由,則更新路由
//便於下面到這個路由的連接池查找
selectedRoute = nextRouteToTry
nextRouteToTry = null
}
}
}
toClose?.closeQuietly()
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection!!)
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result!!)
}
//返回當前連接or緩存池中找到的連接
if (result != null) {
return result!!
}
//如果需要切換路由查找,則切換路由,阻塞操作
var newRouteSelection = false
if (selectedRoute == null && (routeSelection == null || !routeSelection!!.hasNext())) {
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
newRouteSelection = true
routeSelection = localRouteSelector.next()
}
var routes: List<Route>? = null
synchronized(connectionPool) {
if (call.isCanceled()) throw IOException("Canceled")
if (newRouteSelection) {
//如果切換路由了,則在新路由中查找
routes = routeSelection!!.routes
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
foundPooledConnection = true
result = call.connection
}
}
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection!!.next()
}
// 連接池中未找到則創建新的連接
result = RealConnection(connectionPool, selectedRoute!!)
connectingConnection = result
}
}
// 如果第二次連接池查找成功,直接返回連接
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result!!)
return result!!
}
// 連接池查找失敗,調用RealConnection.connect建立連接
// 主要就是TCP中的三次握手建立連接
result!!.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
call.client.routeDatabase.connected(result!!.route())
var socket: Socket? = null
synchronized(connectionPool) {
connectingConnection = null
//多路複用則進行合併
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
result!!.noNewExchanges = true
socket = result!!.socket()
result = call.connection
// 剛創建的連接不可用,在該路由中重試
nextRouteToTry = selectedRoute
} else {
//將連接加入連接池
connectionPool.put(result!!)
call.acquireConnectionNoEvents(result!!)
}
}
socket?.closeQuietly()
eventListener.connectionAcquired(call, result!!)
return result!!
}
總結下ConnectInterceptor的工作流程:
2.5-CallServerInterceptor
在BridgeInterceptor環節中將Request對象轉換爲包含http協議的請求對象,但該對象還需要進一步轉換成我們熟悉的http報文才能進行發起Http請求。先回顧下Http的請求報文:
所以CallServerInterceptor在發起請求前,將request對象encode爲http請求報文。http響應報文decode成response對象
override fun intercept(chain: Interceptor.Chain): Response {
。。。//代碼省略
//寫入請求頭
exchange.writeRequestHeaders(request)
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// 當Header爲Expect: 100-continue時,只發送請求頭
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.
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.
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
//請求結束
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
if (responseBuilder == null) {
//響應頭
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())
.build()
。。。//省略
return response
}
具體的WebSocket建立連接的過程比較複雜篇幅太長,涉及到WebSocket、Source、Sink、okio等內容,可參考其他博文。
3-流程總結
- (1)用戶自定義的Interceptor(可選)。通過OkHttpClient.Builder().addInterceptor添加,實際應用中會在此流程中往request添加一些全局的業務參數,比如cuid、userId、位置信息等
- (2)RetryAndFollowUpInterceptor。開啓無限循環,執行後續攔截器步驟,根據返回的response
- 不需要重試或重定向或者不能再重試、重定向,直接返回response跳出循環
- 重試或重定向,更新request並再次執行後續步驟
- (3)BridgeInterceptor。
- 請求前,將Request對象轉換爲包含http協議信息的請求
- 執行後續攔截器
- 請求後,將返回結果Gzip解壓出響應體內容
- (4)CacheInterceptor。
- 請求前,判斷緩存策略,強制緩存則直接返回緩存,不再發起網絡請求
- 執行後續攔截器
- 請求後,對比緩存策略則使用緩存,否則使用網絡返回response,更新緩存
- (5)ConnectInterceptor
- 判斷當前連接是否可用,不可用則從連接池中獲取連接
- 獲取失敗則新建連接並執行TCP三次握手建立連接
- 執行後續流程
- (6)NetworkInterceptor(可選)。在建立連接後–>正式發送請求前的過程,用戶進行一些處理。
- (7)CallServerIntercepto
- 請求前,將請求request encode爲http協議的報文
- 請求後,從HTTP響應報文decode出response