美文网首页初见
okHttp源码解读(一)

okHttp源码解读(一)

作者: 糖葫芦_倩倩 | 来源:发表于2019-08-28 16:00 被阅读0次

    okHttp 出来有一段时间了,也不是没用过而是没在项目中实际用过,如今新项目就用了这个网络框架,在项目中做了简单的封装和使用,对于源码我过了一遍,主要看整体流程,更容易把握整体架构。

    1. 整体流程

    构建 okHttpClient -------->构建 Request.Builder ----------->发送请求,构建Call(RealCall),有同步(execute)和 异步(enqueue)请求 -------------> Dispatcher
    ---------> InterceptorChain -------------> 获取 response 再返回;

    2. 拦截器

    在看这个源码的时候,我主要看的是这个拦截器部分 ;拦截器得从请求入口那里说起;

    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)处:表示把当前的请求(call)添加到队列中去,(runningSyncCalls :ArrayDeque<RealCall>() )
    • 注释(2)处:getResponseWithInterceptorChain() 调用获取这个 拦截器链并获取结果 返回,主要就是这个方法。

    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 {
          ....
        }
      }
    
    • 在代码的一开始构建了拦截器的集合,interceptors ,分别按照顺序,依次添加了 :
      拦截器1:RetryAndFollowUpInterceptor
      拦截器2: BridgeInterceptor
      拦截器3:CacheInterceptor
      拦截器4:ConnecInterceptor
      拦截器5:CallServerInterceptor
      而后就构建了 RealInterceptorChain ----->chain 在注释(1)处 ;
      在注释(2)处,调用 chain.proceed(originalRequest) 返回 response
      关键方法就是: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)处,我们看到 第四个参数 :index+1 ,之前的时候我们传入的是0 ,这个表示取出下一个 RealInterceptorChain ;
    • 在注释(2)处,取出当前的拦截器:interceptor
    • 在注释(3)处,调用拦截器的 intercept 拦截方法,并将下一个拦截器传入进去 ;
      每一个拦截器都是 继承于 Interceptor 并 重写 interceptor 方法 ;如果要自定义拦截器也是如此;

    2.1 第一个拦截器也就是:RetryAndFollowUpInterceptor

    RetryAndFollowUpInterceptor.kt

    /**关于该拦截器的介绍
     * This interceptor recovers from failures and follows redirects as necessary. It may throw an
     * [IOException] if the call was canceled.
    就是说会从失败中恢复并且在必要的时候重定向,如果请求被取消可能会抛出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) {  ------> 这里是个死循环
          transmitter.prepareToConnect(request)
    
          if (transmitter.isCanceled) {
            throw IOException("Canceled")
          }
    
          var response: Response
          var success = false
          try {
            response = realChain.proceed(request, transmitter, null) --------> 这里链式调用,就传递到下一个拦截器,也是调用下个拦截器的 interceptor 方法
            success = true
          } catch (e: RouteException) {
            // The attempt to connect via a route failed. The request will not have been sent.
            如果路由失败,请求不会再次发送
            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.如果与服务器交流建立链接失败,可能会重新发送请求
            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

    /**就是把用户请求转化为网络(network)请求, 把服务器响应转化为用户友好的响应
     * 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()
      }
    

    这个每个不同的拦截器都有自己不同的处理,这里这个拦截器主要就是对 用户发出的request 转化为服务器需要的 request , 同样 response 也做一定的处理转化;

    • 注释(1)-(6)都是在做这样的转化,看名字:userRequest -----> requestBuilder
    • 注释(7)就又是链式调用下一个拦截器了 得到 networkResponse
    • 下面就是拿到 networkResponse 进行转化了,转变为------> responseBuilder

    2.3 拦截器3:CacheInterceptor

    CacheInterceptor.kt

    /** Serves requests from the cache and writes responses to the cache. */
    从缓存获取请求,并且将response写入缓存 ,这跟你在构建 OKHttpClinent 是否设置缓存有关
    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 根据注释说明 If we're forbidden from using the network and the cache is insufficient, fail 就是说如果我们禁止从网络获取并且也无缓存,那就失败了,构建相应的 response ; body 我们能看到是 EMPTY_RESPONSE ;
    • 注释(3):If we don't need the network, we're done 表示有缓存,如果我们设置了不从网络加载数据,那么也构建相应的 response
    • 注释(4):同样调用下个拦截器的 interceptor 方法 ;
    • 注释(5):判断网络得到的 response ,是否修改,是否要更新缓存,如果修改了就更新缓存 ;
    • 注释(6):构建返回的 response

    2.4 拦截器4:ConnectInterceptor

    ConnectInterceptor.kt

    该拦截器主要就负责与服务器建立连接,然后继续下个拦截器
    /** 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.我们需要此次请求是安全的,可能会先验证一下 get 请求
        val doExtensiveHealthChecks = request.method != "GET"
        val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)
    
        return realChain.proceed(request, transmitter, exchange)
      }
    

    这个建立与服务器建立连接,在 newExchange 中,一直跟着找能发现以下的代码:

    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)
        }
      }
    

    根据不同的 http 协议,分别就是:
    Http1ExchangeCodec : http1.1 ;
    Http2ExchangeCodechttp2.0 ;
    做不同的处理 ;

    2.5 拦截器5:CallServerInterceptor

    CallServerInterceptor.kt

    这是链式调用的最后一个拦截器,会向服务器发送一个请求
    /** 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
      }
    

    基本就是发起网络请求了以及对各种返回码的处理,我在网上找到关于拦截器之间的一个动态图,挺形象的:


    image

    相关文章

      网友评论

        本文标题:okHttp源码解读(一)

        本文链接:https://www.haomeiwen.com/subject/hwlpectx.html