前言
在上一篇文章中我们已经分析过了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的流程还是非常简单的,无非就是对网络传输协议中一些必须参数的封装,这样应用层在调用的时候就省去了这些步骤。