okHttp 出來有一段時(shí)間了,也不是沒用過而是沒在項(xiàng)目中實(shí)際用過,如今新項(xiàng)目就用了這個(gè)網(wǎng)絡(luò)框架隧哮,在項(xiàng)目中做了簡單的封裝和使用艳汽,對于源碼我過了一遍,主要看整體流程,更容易把握整體架構(gòu)静陈。
1. 整體流程
構(gòu)建 okHttpClient
-------->構(gòu)建 Request.Builder
----------->發(fā)送請求秫舌,構(gòu)建Call(RealCall),有同步(execute)和 異步(enqueue)請求 -------------> Dispatcher
---------> InterceptorChain
-------------> 獲取 response
再返回璧眠;
2. 攔截器
在看這個(gè)源碼的時(shí)候责静,我主要看的是這個(gè)攔截器部分 灾螃;攔截器得從請求入口那里說起腰鬼;
RealCall.kt
//無論是同步請求還是異步請求,都差不多,這里以同步為例齿税;
override fun execute(): Response {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.timeoutEnter()
transmitter.callStart()
try {
client.dispatcher.executed(this) ----------->(1)
return getResponseWithInterceptorChain() ------------->(2)
} finally {
client.dispatcher.finished(this)
}
}
- 注釋(1)處:表示把當(dāng)前的請求(call)添加到隊(duì)列中去乌助,
(runningSyncCalls :ArrayDeque<RealCall>() )
- 注釋(2)處:
getResponseWithInterceptorChain()
調(diào)用獲取這個(gè) 攔截器鏈并獲取結(jié)果 返回他托,主要就是這個(gè)方法赏参。
RealCall.kt
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(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis,
client.writeTimeoutMillis) -------->(1)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest) ---------->(2)
if (transmitter.isCanceled) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
...
} finally {
....
}
}
- 在代碼的一開始構(gòu)建了攔截器的集合把篓,
interceptors
,分別按照順序韧掩,依次添加了 :
攔截器1:RetryAndFollowUpInterceptor
攔截器2:BridgeInterceptor
攔截器3:CacheInterceptor
攔截器4:ConnecInterceptor
攔截器5:CallServerInterceptor
而后就構(gòu)建了RealInterceptorChain
----->chain
在注釋(1)處 疗锐;
在注釋(2)處滑臊,調(diào)用chain.proceed(originalRequest)
返回response
關(guān)鍵方法就是:proceed
RealInterceptorChain.kt
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
...
// Call the next interceptor in the chain.
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout) -------->(1)
val interceptor = interceptors[index] ------->(2)
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null") ---------->(3)
// Confirm that the next interceptor made its required call to chain.proceed().
check(exchange == null || 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
}
- 注釋(1)處鬓椭,我們看到 第四個(gè)參數(shù) :index+1 ,之前的時(shí)候我們傳入的是0 小染,這個(gè)表示取出下一個(gè)
RealInterceptorChain
; - 在注釋(2)處氧映,取出當(dāng)前的攔截器:
interceptor
- 在注釋(3)處,調(diào)用攔截器的
intercept
攔截方法律姨,并將下一個(gè)攔截器傳入進(jìn)去 择份;
每一個(gè)攔截器都是 繼承于Interceptor
并 重寫interceptor
方法 荣赶;如果要自定義攔截器也是如此拔创;
2.1 第一個(gè)攔截器也就是:RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor.kt
/**關(guān)于該攔截器的介紹
* This interceptor recovers from failures and follows redirects as necessary. It may throw an
* [IOException] if the call was canceled.
就是說會(huì)從失敗中恢復(fù)并且在必要的時(shí)候重定向慢逾,如果請求被取消可能會(huì)拋出IOException.
*/
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request() ------>取出request
val realChain = chain as RealInterceptorChain
val transmitter = realChain.transmitter()
var followUpCount = 0
var priorResponse: Response? = null --------->重定向前的 response
while (true) { ------> 這里是個(gè)死循環(huán)
transmitter.prepareToConnect(request)
if (transmitter.isCanceled) {
throw IOException("Canceled")
}
var response: Response
var success = false
try {
response = realChain.proceed(request, transmitter, null) --------> 這里鏈?zhǔn)秸{(diào)用侣滩,就傳遞到下一個(gè)攔截器君珠,也是調(diào)用下個(gè)攔截器的 interceptor 方法
success = true
} catch (e: RouteException) {
// The attempt to connect via a route failed. The request will not have been sent.
如果路由失敗材部,請求不會(huì)再次發(fā)送
if (!recover(e.lastConnectException, transmitter, false, request)) {
throw e.firstConnectException
}
continue
} catch (e: IOException) {
// An attempt to communicate with a server failed. The request may have been sent.如果與服務(wù)器交流建立鏈接失敗败富,可能會(huì)重新發(fā)送請求
val requestSendStarted = e !is ConnectionShutdownException
if (!recover(e, transmitter, requestSendStarted, request)) throw e
continue
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException()
}
}
// Attach the prior response if it exists. Such responses never have a body.
這里嘗試獲取之前的response兽叮,如果存在的鹦聪,這樣的response 沒有body,可以看到 body 置為了 null ;
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = response.exchange
val route = exchange?.connection()?.route()
val followUp = followUpRequest(response, route)
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
transmitter.timeoutEarlyExit()
}
return response
}
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
return response
}
response.body?.closeQuietly()
if (transmitter.hasExchange()) {
exchange?.detachWithViolence()
}
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response ----->這里對priResponse 賦值
}
}
2.2 攔截器2:BridgeInterceptor
BridgeInterceptor.kt
/**就是把用戶請求轉(zhuǎn)化為網(wǎng)絡(luò)(network)請求, 把服務(wù)器響應(yīng)轉(zhuǎn)化為用戶友好的響應(yīng)
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/
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()) ----->(1)
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString()) ----->(2)
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())----->(3)
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")----->(4)
}
// 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))----->(5)
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)----->(6)
}
val networkResponse = chain.proceed(requestBuilder.build()) -------->(7)
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()))
}
}
return responseBuilder.build()
}
這個(gè)每個(gè)不同的攔截器都有自己不同的處理规丽,這里這個(gè)攔截器主要就是對 用戶發(fā)出的request 轉(zhuǎn)化為服務(wù)器需要的 request
, 同樣 response
也做一定的處理轉(zhuǎn)化赌莺;
- 注釋(1)-(6)都是在做這樣的轉(zhuǎn)化艘狭,看名字:
userRequest
-----> requestBuilder - 注釋(7)就又是鏈?zhǔn)秸{(diào)用下一個(gè)攔截器了 得到
networkResponse
- 下面就是拿到
networkResponse
進(jìn)行轉(zhuǎn)化了巢音,轉(zhuǎn)變?yōu)?----->responseBuilder
2.3 攔截器3:CacheInterceptor
CacheInterceptor.kt
/** Serves requests from the cache and writes responses to the cache. */
從緩存獲取請求官撼,并且將response寫入緩存 ,這跟你在構(gòu)建 OKHttpClinent 是否設(shè)置緩存有關(guān)
override fun intercept(chain: Interceptor.Chain): Response {
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
cache?.trackResponse(strategy)
if (cacheCandidate != null && cacheResponse == null) { ------->(1)
// 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.
if (networkRequest == null && cacheResponse == null) {-------->(2)
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()
}
// If we don't need the network, we're done.
if (networkRequest == null) {----------> (3)
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build()
}
var networkResponse: Response? = null
try {
networkResponse = chain.proceed(networkRequest) --------> (4)
} 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) {--------->(5)
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()
// 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
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build() ---------> (6)
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response)
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
- 注釋(1):有緩存但是不允許緩存 ;
- 注釋(2):
networkRequest == null && cacheResponse == null
根據(jù)注釋說明If we're forbidden from using the network and the cache is insufficient, fail
就是說如果我們禁止從網(wǎng)絡(luò)獲取并且也無緩存斜筐,那就失敗了顷链,構(gòu)建相應(yīng)的response
; body 我們能看到是EMPTY_RESPONSE
; - 注釋(3):
If we don't need the network, we're done
表示有緩存嗤练,如果我們設(shè)置了不從網(wǎng)絡(luò)加載數(shù)據(jù),那么也構(gòu)建相應(yīng)的response
霜大; - 注釋(4):同樣調(diào)用下個(gè)攔截器的
interceptor
方法 战坤; - 注釋(5):判斷網(wǎng)絡(luò)得到的
response
,是否修改残拐,是否要更新緩存,如果修改了就更新緩存 囊卜; - 注釋(6):構(gòu)建返回的
response
2.4 攔截器4:ConnectInterceptor
ConnectInterceptor.kt
該攔截器主要就負(fù)責(zé)與服務(wù)器建立連接栅组,然后繼續(xù)下個(gè)攔截器
/** Opens a connection to the target server and proceeds to the next interceptor. */
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val request = realChain.request()
val transmitter = realChain.transmitter()
// We need the network to satisfy this request. Possibly for validating a conditional GET.我們需要此次請求是安全的笑窜,可能會(huì)先驗(yàn)證一下 get 請求
val doExtensiveHealthChecks = request.method != "GET"
val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)
return realChain.proceed(request, transmitter, exchange)
}
這個(gè)建立與服務(wù)器建立連接,在 newExchange
中嫌蚤,一直跟著找能發(fā)現(xiàn)以下的代碼:
internal fun newCodec(client: OkHttpClient, chain: Interceptor.Chain): ExchangeCodec {
val socket = this.socket!!
val source = this.source!!
val sink = this.sink!!
val http2Connection = this.http2Connection
return if (http2Connection != null) {
Http2ExchangeCodec(client, this, chain, http2Connection)
} else {
socket.soTimeout = chain.readTimeoutMillis()
source.timeout().timeout(chain.readTimeoutMillis().toLong(), MILLISECONDS)
sink.timeout().timeout(chain.writeTimeoutMillis().toLong(), MILLISECONDS)
Http1ExchangeCodec(client, this, source, sink)
}
}
根據(jù)不同的 http
協(xié)議脱吱,分別就是:
Http1ExchangeCodec
: http1.1
;
Http2ExchangeCodec
:http2.0
;
做不同的處理 箱蝠;
2.5 攔截器5:CallServerInterceptor
CallServerInterceptor.kt
這是鏈?zhǔn)秸{(diào)用的最后一個(gè)攔截器,會(huì)向服務(wù)器發(fā)送一個(gè)請求
/** This is the last interceptor in the chain. It makes a network call to the server. */
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()
exchange.writeRequestHeaders(request)
var responseHeadersStarted = false
var responseBuilder: Response.Builder? = null
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.
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseHeadersStarted = true
exchange.responseHeadersStart()
responseBuilder = exchange.readResponseHeaders(true)
}
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) {
// 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()
}
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart()
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false)!!
}
var response = responseBuilder
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
response = exchange.readResponseHeaders(false)!!
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
exchange.responseHeadersEnd(response)
response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} 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() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
return response
}
基本就是發(fā)起網(wǎng)絡(luò)請求了以及對各種返回碼的處理矾克,我在網(wǎng)上找到關(guān)于攔截器之間的一個(gè)動(dòng)態(tài)圖胁附,挺形象的: