Android OkHttp source code reading notes (5)

OkHttp source code reading notes (5)

The first article introduces OkHttpthe synchronous calls and asynchronous calls, Dispatcherthe working mode of the task scheduler and RealInterceptorChainthe working mode of the interceptor chain: Android OkHttp source code reading notes (1) .
The second article introduces OkHttphow to get the link from the cache, how to create the link and ConnectionPoolthe working principle of: Android OkHttp source code reading notes (2)
The third article introduces OkHttpthe system interceptor in RetryAndFollowUpInterceptorand BridgeInterceptor: Android OkHttp source code reading notes ( three) .
The fourth article introduces OkHttpthe 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

CallServerInterceptorThe system interceptor in front of has been processed , and the processing logic Requestto be returned later Responseis also ready. Work related to network links has also been taken care of. So CallServerInterceptorthe job is to Requesttransmit the data in to the server through the network link, and at the same time receive Responsethe 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:

  1. Call Exchange#writeRequestHeaders()the method to write Request Header, and then analyze this method later.
  2. Write Request Body.
    • Requests other than and can only be GETwritten if is not empty.HEADRequest Body
    • If Request Headerthere is a that Expect: 100-continuecannot be written Request Body, it means that you need to wait for Responsethe return from 100+before performing subsequent operations.
    • Exchange#createRequestBody()Create a corresponding Sink(can be understood as ) through the (subsequent detailed analysis) method OutputStream, and then Request Bodywrite the above Sink.
  3. Read through Exchange#readResponseHeaders()the (detailed analysis later) method Response Header.
  4. Open through Exchange#openResponseBody()the (subsequent detailed analysis) method Response Body. Note that it is not read Bodyinto the memory. It will only be read when the user of the library actually uses it.
  5. Determine Request Headerwhether Response Heanderthere is or in Connection: close, which means that the link cannot be reused after use. By calling to mark, the attribute Exchange#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#noNewExchangestrue

Let’s briefly explain the situation Response Codefor 100+:

  1. If Request Headerthere is in Expect: 100-continue, it means that it needs to wait Response Codefor 100+before continuing the subsequent operation.
  2. If Response Headeryes is returned in 100+, you need to read it again Response Header.

Specifically Repsonse Codefor 100+and Expect: 100-continuethis situation, if you are not familiar with it, it is recommended to go online to find Httpthe relevant description of the protocol.

Exchange#writeRequestHeaders()

Let me give an Request Headerexample 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
    }
  }

CodecThere are two implementation classes above Http1ExchangeCodecand Http2ExchangeCodecrepresent the writing logic of Http 1the and Http 2protocols 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.:ValueContent-Type: application/jsonHeaderRequest BodySinkOutputStreamSourceInputStreamOkIoOkHttp

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 Sinkobject and then creating an RequestBodySinkobject. 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-Lengthwill be called to create , otherwise the method will be called. Let's take as an example and look at the subsequent source code:newKnownLengthSink()SinknewChunkedSink()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
    }
  }

KnownLengthSinkThe object is Http1ExchangeCodecan 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 Socketcreated Sink.

Let's take a look at RequestBodySinkthe 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)
    }
  }

RequestBodySinkis Exchangean 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 Bodywill be called . This bodyComplete()The method will eventually be notified to RealCallthe object. Response BodyWhen the reading is completed, this method will also be called, which will be analyzed separately later.

Exchange#readResponseHeaders()

Next, look at Response Headerthe 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 Responseexample:

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 Headerreading 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 Headerhas been read. This Request Headeris similar to and the following content is corresponding Response Body.

Exchange#openResponseBody()

Now continue to look Response Bodyat 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 ResponseBodySourceat 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)
    }
  }

ResponseBodySourceA 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 RequestBodySinkare similar , but it should be noted here ResponseBodySourcethat it will only read when the data is actually used in the calling place of the library. ResponseThe data here is not read when returning. This is the same as CachedInterceptorthe 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, RealConnectionwill be added to RealCall, and the corresponding RealCallreference will also be added to RealConnection. So when will the reference RealConnectionin RealCallbe removed? ? ? It seems that we did not see this part of the code, Responsenor did we find this part of the code when returning.
In fact, the reference in RealCallwill 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:RealConnectionResponseRealCallRequest BodyResponse BodyExchange#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 eventListenervarious 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 RealCallto Connection. If the returned Socketis 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#callsremove the reference in , and then determine RealConnection#callswhether it is empty. If it is already empty, it means that RealConnectionit is currently idle, and then notify ConnectionPool#connectionBecameIdle()that if the returned by this method Socketis not empty, it means that it needs to be released. This is related Request Headerto and Response Header. If they are specified Connection: close, it means that the link will not be reused, and then RealConnection#noNewExchangesthe object will be set to true(you can see this part CallServerInterceptor), and then the above Socketobject 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 Responsereturns successfully, the request is not completed and you need to wait for Response Bodythe processing to be completed (reading is completed or an exception occurs). If you do not need Response Bodythe data inside It can be turned off manually. Through the above operations, you can remove the reference fromRealCall . RealConnectionIf the above operations are not done, RealCallreference leakage may occur. If reference leakage occurs, it OkHttpcan also be detected. RealConnectionPoolThere will be an idle detector Connection( cleanupTaskdetailed 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 OkHttpa 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.
img
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)

PS: There is also a ChatGPT robot in the group, which can answer everyone’s work or technical questions.

picture

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/135092270