OkHttp source code reading notes (5)
The first article introduces OkHttp
the synchronous calls and asynchronous calls, Dispatcher
the working mode of the task scheduler and RealInterceptorChain
the working mode of the interceptor chain: Android OkHttp source code reading notes (1) .
The second article introduces OkHttp
how to get the link from the cache, how to create the link and ConnectionPool
the working principle of: Android OkHttp source code reading notes (2)
The third article introduces OkHttp
the system interceptor in RetryAndFollowUpInterceptor
and BridgeInterceptor
: Android OkHttp source code reading notes ( three) .
The fourth article introduces OkHttp
the system interceptor in CacheInterceptor
: Android OkHttp source code reading notes (4) .
This article is the fifth in the series and the last in the series, introducing the last system interceptor CallServerInterceptor
.
CallServerInterceptor
CallServerInterceptor
The system interceptor in front of has been processed , and the processing logic Request
to be returned later Response
is also ready. Work related to network links has also been taken care of. So CallServerInterceptor
the job is to Request
transmit the data in to the server through the network link, and at the same time receive Response
the relevant data sent by the server through the network link, and then return to the upper layer interceptor. Similarly, intercept()
the method is used as the entry function to analyze the process:
@Throws(IOException::class)
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 {
// 写入 Request Header
exchange.writeRequestHeaders(request)
// 判断是否能够写入 Request Header,只有 PUT 和 GET 不允许写入。
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.
// 如果 Reqeust Header 中有 "Expect: 100-continue",表示不能够写入 Request Body,需要等待 Response 返回 100+。后面会看到这部分逻辑。
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
// 直接读 Response Header
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
// 以下是 Http2 相关逻辑跳过
if (requestBody.isDuplex()) {
// ...
} else {
// Http 1.x 相关逻辑
// Write the request body if the "Expect: 100-continue" expectation was met.
// 写入 Request Body。
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()
}
} 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) {
// 读取 Response Header
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()
var code = response.code
// 判断 Response Code 是不是 100+,如果是的话还需要再读取一次 Response Header。
if (shouldIgnoreAndWaitForRealResponse(code)) {
// 再次读取 Response Header。
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)
response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
// WebSocket 逻辑,没有 Response Body。
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
// 构建 Response Body,注意这里的 Response Body 并没有读取到内存中,需要库的使用者真正使用时才会去读取,后续会分析这部分代码。
.body(exchange.openResponseBody(response))
.build()
}
// 如果 Request Header 或者 Response Header 中有 "Connection: close" 就表示后续不能够再使用这个链接,然后会把这个链接标记为不可用。
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
} catch (e: IOException) {
if (sendRequestException != null) {
sendRequestException.addSuppressed(e)
throw sendRequestException
}
throw e
}
}
Let’s deal with the general logic first:
- Call
Exchange#writeRequestHeaders()
the method to writeRequest Header
, and then analyze this method later. - Write
Request Body
.- Requests other than and can only be
GET
written if is not empty.HEAD
Request Body
- If
Request Header
there is a thatExpect: 100-continue
cannot be writtenRequest Body
, it means that you need to wait forResponse
the return from100+
before performing subsequent operations. Exchange#createRequestBody()
Create a correspondingSink
(can be understood as ) through the (subsequent detailed analysis) methodOutputStream
, and thenRequest Body
write the aboveSink
.
- Requests other than and can only be
- Read through
Exchange#readResponseHeaders()
the (detailed analysis later) methodResponse Header
. - Open through
Exchange#openResponseBody()
the (subsequent detailed analysis) methodResponse Body
. Note that it is not readBody
into the memory. It will only be read when the user of the library actually uses it. - Determine
Request Header
whetherResponse Heander
there is or inConnection: close
, which means that the link cannot be reused after use. By calling to mark, the attributeExchange#noNewExchangesOnConnection()
will eventually be set to . If you have read the second article in this series, you should still have an impression of this parameter.RealConnection#noNewExchanges
true
Let’s briefly explain the situation Response Code
for 100+
:
- If
Request Header
there is inExpect: 100-continue
, it means that it needs to waitResponse Code
for100+
before continuing the subsequent operation. - If
Response Header
yes is returned in100+
, you need to read it againResponse Header
.
Specifically Repsonse Code
for 100+
and Expect: 100-continue
this situation, if you are not familiar with it, it is recommended to go online to find Http
the relevant description of the protocol.
Exchange#writeRequestHeaders()
Let me give an Request Header
example here to help you read the source code below:
GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
Host: Linode.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.8) Gecko/20091102 Firefox/3.5.5
Accept: text/html,application/xhtml+xml,
Accept-Language: en-us
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8
Cache-Control: no-cache
Then look at Exhcnage#writeRequestHeaders()
the source code of the method:
@Throws(IOException::class)
fun writeRequestHeaders(request: Request) {
try {
// 通知 eventListener 写 RequestHeader 开始
eventListener.requestHeadersStart(call)
// 调用 Codec#writeRequestHeaders() 方法写入 Requst Header
codec.writeRequestHeaders(request)
// 通知 eventListener 写 RequestHeader 结束
eventListener.requestHeadersEnd(call, request)
} catch (e: IOException) {
eventListener.requestFailed(call, e)
trackFailure(e)
throw e
}
}
Codec
There are two implementation classes above Http1ExchangeCodec
and Http2ExchangeCodec
represent the writing logic of Http 1
the and Http 2
protocols respectively, and our subsequent analysis will also focus on them Http1ExchangeCodec
.
Let's continue to look at Http1ExchangeCodec#writeRequestHeaders()
the implementation of the method:
override fun writeRequestHeaders(request: Request) {
// 获取 Request 方法,协议,路径等等字符串,例如:GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
val requestLine = RequestLine.get(request, connection.route().proxy.type())
writeRequest(request.headers, requestLine)
}
/** Returns bytes of a request header for sending on an HTTP transport. */
fun writeRequest(headers: Headers, requestLine: String) {
check(state == STATE_IDLE) {
"state: $state" }
// 写入协议行
sink.writeUtf8(requestLine).writeUtf8("\r\n")
// 写入 Rquest Header,单个 Header 以换行符结束。
for (i in 0 until headers.size) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n")
}
// 再写入一个空的行,用来标记 Header 已经写完了,后续就是写 Request Body。
sink.writeUtf8("\r\n")
state = STATE_OPEN_REQUEST_BODY
}
The first line is the protocol-related line, and the format is 请求方法
+ + 请求路径
+ + 协议
, for example: GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
. The follow-up is writing Request Header
, the format is ++ Key
, for example . When is finished writing, a blank line will be written to indicate that it is finished, and the subsequent writing will be. (If any) The object here will be created after the link is successfully built. It is equivalent to writing, and corresponding to reading is equivalent to . They are all implementations of . Introduced earlier when the link is relevant.:
Value
Content-Type: application/json
Header
Request Body
Sink
OutputStream
Source
InputStream
OkIo
OkHttp
Exchange#createRequestBody()
Here's another look Exchange#createRequestBody()
at the implementation:
@Throws(IOException::class)
fun createRequestBody(request: Request, duplex: Boolean): Sink {
this.isDuplex = duplex
// 获取 Request Body 的长度
val contentLength = request.body!!.contentLength()
// 通知 eventListener 开始写 Request Body
eventListener.requestBodyStart(call)
val rawRequestBody = codec.createRequestBody(request, contentLength)
return RequestBodySink(rawRequestBody, contentLength)
}
The above code is relatively simple, mainly by Codec#createRequestBody()
constructing an Sink
object and then creating an RequestBodySink
object. Let’s first look at Http1ExchangeCodec#createRequestBody()
the implementation of the method:
override fun createRequestBody(request: Request, contentLength: Long): Sink {
return when {
request.body != null && request.body.isDuplex() -> throw ProtocolException(
"Duplex connections are not supported for HTTP/1")
request.isChunked -> newChunkedSink() // Stream a request body of unknown length.
contentLength != -1L -> newKnownLengthSink() // Stream a request body of a known length.
else -> // Stream a request body of a known length.
throw IllegalStateException(
"Cannot stream a request body without chunked encoding or a known content length!")
}
}
If there is , the method Content-Length
will be called to create , otherwise the method will be called. Let's take as an example and look at the subsequent source code:newKnownLengthSink()
Sink
newChunkedSink()
newKnownLengthSink()
private fun newKnownLengthSink(): Sink {
check(state == STATE_OPEN_REQUEST_BODY) {
"state: $state" }
state = STATE_WRITING_REQUEST_BODY
return KnownLengthSink()
}
private inner class KnownLengthSink : Sink {
// 写超时对象
private val timeout = ForwardingTimeout(sink.timeout())
private var closed: Boolean = false
override fun timeout(): Timeout = timeout
override fun write(source: Buffer, byteCount: Long) {
check(!closed) {
"closed" }
// 检查 byteCount 是否已经超过了 Buffer 的最大值
checkOffsetAndCount(source.size, 0, byteCount)
// 直接写入到链接的 Sink 中
sink.write(source, byteCount)
}
override fun flush() {
if (closed) return // Don't throw; this stream might have been closed on the caller's behalf.
sink.flush()
}
override fun close() {
if (closed) return
closed = true
// 关闭超时
detachTimeout(timeout)
state = STATE_READ_RESPONSE_HEADERS
}
}
KnownLengthSink
The object is Http1ExchangeCodec
an internal class in and its processing method is very simple, with only some simple status judgments. When the writing is completed, the write timeout timer will be turned off, and finally written to when the link is successfully Socket
created Sink
.
Let's take a look at RequestBodySink
the implementation in :
private inner class RequestBodySink(
// 这个就是上面分析的 KnownLengthSink。
delegate: Sink,
/** The exact number of bytes to be written, or -1L if that is unknown. */
private val contentLength: Long
) : ForwardingSink(delegate) {
private var completed = false
private var bytesReceived = 0L
private var closed = false
@Throws(IOException::class)
override fun write(source: Buffer, byteCount: Long) {
check(!closed) {
"closed" }
// 检查写的 Request Body 是否超过了设置的长度
if (contentLength != -1L && bytesReceived + byteCount > contentLength) {
throw ProtocolException(
"expected $contentLength bytes but received ${bytesReceived + byteCount}")
}
try {
super.write(source, byteCount)
this.bytesReceived += byteCount
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun flush() {
try {
super.flush()
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun close() {
if (closed) return
closed = true
// 检查写的长度
if (contentLength != -1L && bytesReceived != contentLength) {
throw ProtocolException("unexpected end of stream")
}
try {
super.close()
// 通知写完成
complete(null)
} catch (e: IOException) {
throw complete(e)
}
}
private fun <E : IOException?> complete(e: E): E {
if (completed) return e
completed = true
// 这个方法非常重要,最终会通知 RealCall,写 Request Body 已经完成,这个方法也会通知读 Response Body 已经完成。
return bodyComplete(bytesReceived, responseDone = false, requestDone = true, e = e)
}
}
RequestBodySink
is Exchange
an internal class in and its processing method is also very simple. It simply determines the length of the written . If the length is wrong, an exception will be thrown. Then there is a very important point. After the writing is completed , the method Request Body
will be called . This bodyComplete()
The method will eventually be notified to RealCall
the object. Response Body
When the reading is completed, this method will also be called, which will be analyzed separately later.
Exchange#readResponseHeaders()
Next, look at Response Header
the implementation of reading:
@Throws(IOException::class)
fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
try {
val result = codec.readResponseHeaders(expectContinue)
result?.initExchange(this)
return result
} catch (e: IOException) {
// 通知 eventListener 读取 header 失败
eventListener.responseFailed(call, e)
trackFailure(e)
throw e
}
}
The above code is very simple, let's continue to look Http1ExchangeCodec#readResponseHeaders()
at the implementation:
In order to better understand the source code, here is an Response
example:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 88
Content-Type: text/html
Connection: Closed
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
// 状态检查
check(state == STATE_OPEN_REQUEST_BODY ||
state == STATE_WRITING_REQUEST_BODY ||
state == STATE_READ_RESPONSE_HEADERS) {
"state: $state"
}
try {
// 读取 Response 的状态行,例如:HTTP/1.1 200 OK
val statusLine = StatusLine.parse(headersReader.readLine())
val responseBuilder = Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
// 读取 header
.headers(headersReader.readHeaders())
return when {
expectContinue && statusLine.code == HTTP_CONTINUE -> {
null
}
statusLine.code == HTTP_CONTINUE -> {
state = STATE_READ_RESPONSE_HEADERS
responseBuilder
}
statusLine.code in (102 until 200) -> {
// Processing and Early Hints will mean a second headers are coming.
// Treat others the same for now
state = STATE_READ_RESPONSE_HEADERS
responseBuilder
}
else -> {
state = STATE_OPEN_RESPONSE_BODY
responseBuilder
}
}
} catch (e: EOFException) {
// Provide more context if the server ends the stream before sending a response.
val address = connection.route().address.url.redact()
throw IOException("unexpected end of stream on $address", e)
}
}
First read the status line: 协议
+ + Response Code
+ + Response Message
, for example: HTTP/1.1 200 OK
, and the final Response Header
reading will call HeadersReader#readHeaders()
the method. Let's take a look at its implementation:
/** Reads headers or trailers. */
fun readHeaders(): Headers {
// 构建 header builder 对象
val result = Headers.Builder()
while (true) {
// 读取行
val line = readLine()
// 如果读取到的行为空,表示 header 读取已经结束
if (line.isEmpty()) break
// 通过方法解析 Response Header
result.addLenient(line)
}
// 构建 Headers 对象。
return result.build()
}
The above method is also very simple. A line of string represents one Response Header
. Headers.Builder#addLenient()
The string is parsed through the method. If the string is empty, it means that it Response Header
has been read. This Request Header
is similar to and the following content is corresponding Response Body
.
Exchange#openResponseBody()
Now continue to look Response Body
at the processing method:
@Throws(IOException::class)
fun openResponseBody(response: Response): ResponseBody {
try {
val contentType = response.header("Content-Type")
val contentLength = codec.reportedContentLength(response)
// 构建 Response Body 的 Source
val rawSource = codec.openResponseBodySource(response)
// 再构建一个新的 Source
val source = ResponseBodySource(rawSource, contentLength)
// 构建新的 Response Body。
return RealResponseBody(contentType, contentLength, source.buffer())
} catch (e: IOException) {
eventListener.responseFailed(call, e)
trackFailure(e)
throw e
}
}
Continue to look at Http1ExchangeCodec#openResponseBodySource()
the implementation of the method:
override fun openResponseBodySource(response: Response): Source {
return when {
!response.promisesBody() -> newFixedLengthSource(0)
response.isChunked -> newChunkedSource(response.request.url)
else -> {
val contentLength = response.headersContentLength()
if (contentLength != -1L) {
// 有固定长度的 Response Body
newFixedLengthSource(contentLength)
} else {
// 无固定长度的 Response Body
newUnknownLengthSource()
}
}
}
}
Like Request Body
, there are two types: fixed length and no fixed length. Let's take the fixed length as an example to see newFixedLengthSource()
the implementation of the method:
private fun newFixedLengthSource(length: Long): Source {
check(state == STATE_OPEN_RESPONSE_BODY) {
"state: $state" }
state = STATE_READING_RESPONSE_BODY
return FixedLengthSource(length)
}
An object is constructed here FixedLengthSource
, let's take a look at its implementation:
private inner class FixedLengthSource(private var bytesRemaining: Long) :
AbstractSource() {
init {
if (bytesRemaining == 0L) {
// 如果长度为 0,直接通知读 Response Body 已经完成
responseBodyComplete()
}
}
override fun read(sink: Buffer, byteCount: Long): Long {
// 读的数量判断
require(byteCount >= 0L) {
"byteCount < 0: $byteCount" }
// 状态判断
check(!closed) {
"closed" }
if (bytesRemaining == 0L) return -1
// 读 Sink
val read = super.read(sink, minOf(bytesRemaining, byteCount))
if (read == -1L) {
connection.noNewExchanges() // The server didn't supply the promised content length.
val e = ProtocolException("unexpected end of stream")
responseBodyComplete()
throw e
}
bytesRemaining -= read
if (bytesRemaining == 0L) {
// 已经读取完毕
responseBodyComplete()
}
return read
}
override fun close() {
if (closed) return
if (bytesRemaining != 0L &&
!discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
connection.noNewExchanges() // Unread bytes remain on the stream.
// 读取完毕
responseBodyComplete()
}
closed = true
}
}
The above code is also relatively simple. It simply determines the size status of the read and the switch status of the stream.
Let’s take a brief look ResponseBodySource
at the implementation:
internal inner class ResponseBodySource(
// 上面的 FixedLengthSource 对象
delegate: Source,
private val contentLength: Long
) : ForwardingSource(delegate) {
private var bytesReceived = 0L
private var invokeStartEvent = true
private var completed = false
private var closed = false
init {
// 如果长度为 0 就直接完成
if (contentLength == 0L) {
complete(null)
}
}
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
// 关状态判断
check(!closed) {
"closed" }
try {
// 真正的读
val read = delegate.read(sink, byteCount)
if (invokeStartEvent) {
invokeStartEvent = false
// 通知 eventListener Response 读开始
eventListener.responseBodyStart(call)
}
if (read == -1L) {
// 已经读取完毕
complete(null)
return -1L
}
val newBytesReceived = bytesReceived + read
if (contentLength != -1L && newBytesReceived > contentLength) {
throw ProtocolException("expected $contentLength bytes but received $newBytesReceived")
}
bytesReceived = newBytesReceived
if (newBytesReceived == contentLength) {
// 读取完毕
complete(null)
}
return read
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun close() {
if (closed) return
closed = true
try {
super.close()
// 读取完毕
complete(null)
} catch (e: IOException) {
throw complete(e)
}
}
fun <E : IOException?> complete(e: E): E {
if (completed) return e
completed = true
// If the body is closed without reading any bytes send a responseBodyStart() now.
if (e == null && invokeStartEvent) {
invokeStartEvent = false
eventListener.responseBodyStart(call)
}
// 通知 RealCall Response Body 读取完毕
return bodyComplete(bytesReceived, responseDone = true, requestDone = false, e = e)
}
}
ResponseBodySource
A simple status judgment is also made in . A very important method here is that after the reading is completed, after early closing and after an exception occurs, the complete()
method will be called, and then bodyComplete()
the method will be called to notify RealCall
. Here and RequestBodySink
are similar , but it should be noted here ResponseBodySource
that it will only read when the data is actually used in the calling place of the library. Response
The data here is not read when returning. This is the same as CachedInterceptor
the returned cache .Response Body
A sign indicating the end of an HTTP request
When analyzing the process of creating a network link in the previous article, it was mentioned that after the creation of the network link is completed, RealConnection
will be added to RealCall
, and the corresponding RealCall
reference will also be added to RealConnection
. So when will the reference RealConnection
in RealCall
be removed? ? ? It seems that we did not see this part of the code, Response
nor did we find this part of the code when returning.
In fact, the reference in RealCall
will be cleared when it is truly completed . When it returns, it is not truly completed. Instead, the request is marked as completed when the requested writing and reading are completed. In the above section, we analyzed them After the reading and writing is completed, the method will eventually be called. Let's take a look at its code implementation:RealConnection
Response
RealCall
Request Body
Response Body
Exchange#bodyComplete()
fun <E : IOException?> bodyComplete(
bytesRead: Long,
responseDone: Boolean,
requestDone: Boolean,
e: E
): E {
if (e != null) {
// 记录失败
trackFailure(e)
}
if (requestDone) {
if (e != null) {
// 通知 eventListener 请求 Request 失败
eventListener.requestFailed(call, e)
} else {
// 通知 eventListener 请求 Request Body 结束
eventListener.requestBodyEnd(call, bytesRead)
}
}
if (responseDone) {
if (e != null) {
// 通知 eventListener 请求 Response 失败
eventListener.responseFailed(call, e)
} else {
// 通知 eventListener 请求 Response Body 结束
eventListener.responseBodyEnd(call, bytesRead)
}
}
// 通知 RealCall
return call.messageDone(this, requestDone, responseDone, e)
}
The above code simply notifies eventListener
various request statuses, which mainly calls RealCall#messageDone()
the method. Let's take a look at its implementation:
internal fun <E : IOException?> messageDone(
exchange: Exchange,
requestDone: Boolean,
responseDone: Boolean,
e: E
): E {
if (exchange != this.exchange) return e // This exchange was detached violently!
var bothStreamsDone = false
var callDone = false
synchronized(this) {
// 各种状态的更新
if (requestDone && requestBodyOpen || responseDone && responseBodyOpen) {
if (requestDone) requestBodyOpen = false
if (responseDone) responseBodyOpen = false
bothStreamsDone = !requestBodyOpen && !responseBodyOpen
callDone = !requestBodyOpen && !responseBodyOpen && !expectMoreExchanges
}
}
if (bothStreamsDone) {
this.exchange = null
// 增加成功次数
this.connection?.incrementSuccessCount()
}
if (callDone) {
// 完成
return callDone(e)
}
return e
}
If the method will be called after the request is completed callDone()
, let's continue to look at:
private fun <E : IOException?> callDone(e: E): E {
assertThreadDoesntHoldLock()
val connection = this.connection
if (connection != null) {
connection.assertThreadDoesntHoldLock()
val socket = synchronized(connection) {
// 检查 Connection 状态,如果返回 Socket 不为空,就表示要手动释放。
releaseConnectionNoEvents() // Sets this.connection to null.
}
if (this.connection == null) {
socket?.closeQuietly()
// 通知 eventListener 链接已经释放
eventListener.connectionReleased(this, connection)
} else {
check(socket == null) // If we still have a connection we shouldn't be closing any sockets.
}
}
val result = timeoutExit(e)
if (e != null) {
// 通知 eventListener 请求失败
eventListener.callFailed(this, result!!)
} else {
// 通知 event Listener 请求结束
eventListener.callEnd(this)
}
return result
}
The above code will call releaseConnectionNoEvents()
the method to release the reference RealCall
to Connection
. If the returned Socket
is not empty, it means that the link needs to be released manually. Let’s take a look at its source code implementation:
internal fun releaseConnectionNoEvents(): Socket? {
val connection = this.connection!!
connection.assertThreadHoldsLock()
val calls = connection.calls
val index = calls.indexOfFirst {
it.get() == this@RealCall }
check(index != -1)
// 将当前 RealCall 从 RealConnection 的引用中移除
calls.removeAt(index)
// 当前 RealCall 停止对 Connection 引用
this.connection = null
// 如果 RealConnection 中的引用 RealCall 变成了空,就表示它已经闲置了
if (calls.isEmpty()) {
connection.idleAtNs = System.nanoTime()
// 通知 ConnectionPool,当前 connection 已经闲置。
if (connectionPool.connectionBecameIdle(connection)) {
// 需要把当前链接真正关闭。
return connection.socket()
}
}
// 不需要真正关闭链接
return null
}
First RealConnection#calls
remove the reference in , and then determine RealConnection#calls
whether it is empty. If it is already empty, it means that RealConnection
it is currently idle, and then notify ConnectionPool#connectionBecameIdle()
that if the returned by this method Socket
is not empty, it means that it needs to be released. This is related Request Header
to and Response Header
. If they are specified Connection: close
, it means that the link will not be reused, and then RealConnection#noNewExchanges
the object will be set to true
(you can see this part CallServerInterceptor
), and then the above Socket
object will not be empty.
Let's take a look at RealConnectionPool#connectionBecameIdle()
the method:
fun connectionBecameIdle(connection: RealConnection): Boolean {
connection.assertThreadHoldsLock()
return if (connection.noNewExchanges || maxIdleConnections == 0) {
// 需要关闭链接
connection.noNewExchanges = true
connections.remove(connection)
if (connections.isEmpty()) cleanupQueue.cancelAll()
true
} else {
// 不需要关闭,开启一个新的清除任务 Task。
cleanupQueue.schedule(cleanupTask)
false
}
}
I also analyzed the above method in the second article, and it is relatively simple.
Here, the relevant logic of request end has been analyzed. You can make a simple summary: when the request Response
returns successfully, the request is not completed and you need to wait for Response Body
the processing to be completed (reading is completed or an exception occurs). If you do not need Response Body
the data inside It can be turned off manually. Through the above operations, you can remove the reference fromRealCall
. RealConnection
If the above operations are not done, RealCall
reference leakage may occur. If reference leakage occurs, it OkHttp
can also be detected. RealConnectionPool
There will be an idle detector Connection
( cleanupTask
detailed analysis in the second article), and he will call the following method. :
private fun pruneAndGetAllocationCount(connection: RealConnection, now: Long): Int {
connection.assertThreadHoldsLock()
val references = connection.calls
var i = 0
while (i < references.size) {
val reference = references[i]
// 引用不为空就表示没有泄漏
if (reference.get() != null) {
i++
continue
}
// 后续就表示泄漏了
// We've discovered a leaked call. This is an application bug.
val callReference = reference as CallReference
val message = "A connection to ${connection.route().address.url} was leaked. " +
"Did you forget to close a response body?"
Platform.get().logCloseableLeak(message, callReference.callStackTrace)
// 主动移除泄漏的 RealCall
references.removeAt(i)
connection.noNewExchanges = true
// If this was the last allocation, the connection is eligible for immediate eviction.
if (references.isEmpty()) {
connection.idleAtNs = now - keepAliveDurationNs
return 0
}
}
return references.size
}
Leak problems can be detected through the above methods.
at last
After five articles, I have finally finished everything OkHttp
. If you have the patience to read it, I believe you will have OkHttp
a new understanding of it.
at last
If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, you must be able to select and expand, and improve your programming thinking. In addition, good career planning is also very important, and learning habits are important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.
If you have no direction, here is a set of "Advanced Notes on the Eight Modules of Android" written by a senior architect at Alibaba to help you systematically organize messy, scattered, and fragmented knowledge, so that you can systematically and efficiently Master various knowledge points of Android development.
Compared with the fragmented content we usually read, the knowledge points in this note are more systematic, easier to understand and remember, and are strictly arranged according to the knowledge system.
Welcome everyone to support with one click and three links. If you need the information in the article, just scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓ (There is also a small bonus of ChatGPT robot at the end of the article, don’t miss it)