再学Android:OkHttp源码探究(四)BridgeInterceptor

240 阅读3分钟

前言

在上一篇文章中我们已经分析过了ok内置五个拦截器中的第一个RetryAndFollowUpInterceptor,本篇我们将继续按照顺序去探究一下BridgeInterceptor。

介绍

首先来看一下类注释:

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.

释义:应用层和网络层之间的桥接,首先往用户发起的请求中添加一部分网络层需要的参数,接着调用网络,最后从响应体中解析出用户请求的结果
接着我们直接上代码:
  @Throws(IOException::class)
  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())
      }
      val contentLength = body.contentLength()
      if (contentLength != -1L) {
        requestBuilder.header("Content-Length", contentLength.toString())
        requestBuilder.removeHeader("Transfer-Encoding")
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked")
        requestBuilder.removeHeader("Content-Length")
      }
    }
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", userRequest.url.toHostHeader())
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive")
    }

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

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", userAgent)
    }

    val networkResponse = chain.proceed(requestBuilder.build())

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

相对于上一篇的重试拦截器,bridge拦截器的代码相对来说是非常容易看懂的。我们将按照interceptor的调用来分为两个阶段分析:

Request阶段

  • 取出body的contentType设置到header里面

  • 获取请求体的长度放入header

    • Transfer-Encoding 字面意思也很好理解,即为传输过程中的编码格式 根据网上资料:首部字段Transfer-Encoding规定了传输报文主体时采用的编码方式。HTTP1.1的传输编码方式仅对分块传输编码有效。值为 chunked 表示请求体的内容大小是未知的。 因此Transfer-Encoding与Content-Length两个首部不能共存。
  • 将请求的host放入到header里面

    • Host即对应请求中的域名 对此HttpUrl中有详细的注释解释

      URL host()
      http://android.com/ "android.com"
      http://127.0.0.1/ "127.0.0.1"
      http://[::1]/ "::1"
      http://xn--n3h.net/ "xn--n3h.net"
    • 其中toHostHeader()方法会取到对应的域名并且拼接上默认的端口 http默认端口80 、https默认端口443

  • 指定连接模式为 Keep-Alive 这样就能方便的复用连接池。

  • 如果用户没有设置Accept-Encoding,那么会指定数据接收格式为gzip。顺带提一下:gzip格式压缩能大大减少网络流量的消耗。

  • header中放入cookie

  • 请求头中放入 user-agent 如果用户没有设置,那么放入默认 agent:okhttp/x.x.x

Response阶段

  • 首先是根据url拿到返回的cookie。
  • 如果Content-Encoding是gzip类型,并且content-length >0 取出body进行处理
  • 对请求响应体做一遍处理,因为响应体的body的传输格式是gzip类型,通过封装的方法类解析处理
  • 解析成为用户真正可使用的body

总结

以上就是整个BridgeInterceptor的全部流程了。大概可以分为三步

  • 对用户的请求做一遍处理,如果用户设置了参数使用用户配置,如果未设置那么就使用默认的。
  • 通过chain调用下一个拦截器。
  • 对接口的请求做一遍处理,比如gzip、cookie 最后解析成用户真正可使用的body。

可以看到整个BridgeInterceptor的流程还是非常简单的,无非就是对网络传输协议中一些必须参数的封装,这样应用层在调用的时候就省去了这些步骤。