【Android】OkHttp 源码剖析系列(二)——拦截器大体流程分析


之前的文章介绍到了 OkHttp 的拦截器机制的整体概述,现在让我们依次研究一下其拦截器的实现。

本源码剖析系列基于 OkHttp 3.14

文章目录:

【Android】OkHttp 源码剖析系列(一)——请求的发起及拦截器机制概述

【Android】OkHttp 源码剖析系列(二)——拦截器大体流程分析

【Android】OkHttp 源码剖析系列(三)——缓存机制

【Android】OkHttp 源码剖析系列(四)——连接的建立概述

【Android】OkHttp 源码剖析系列(五)——路由选择与代理机制

【Android】OkHttp 源码剖析系列(六)——连接复用机制及连接的建立

【Android】OkHttp 源码剖析系列(七)——请求的发起及响应的读取

RetryAndFollowUpInterceptor

前面提到,RetryAndFollowUpInerceptor 负责了 HTTP 请求的重定向功能,那让我们先了解一下 HTTP 协议中的重定向。

HTTP 中的重定向

HTTP 协议提供了一种重定向的功能,它通过由服务器返回特定格式的响应从而触发客户端的重定向。其对应的 Response Code 格式为 3XX,并且会在 Response Header 的 Location 字段中放入新的 URL,这样我们客户端就可以根据该 Location 字段所指定的 URL 重新请求从而得到需要的数据。

其过程如下图所示:

img

其中重定向对应的状态码及含义如下表所示(摘自维基百科):

image-20190730211626735

重定向与服务器转发的区别

可以发现,重定向和服务器转发请求是有些相似的,它们有什么不同呢?

  1. 重定向是客户端行为,而服务器转发则是服务端行为

  2. 重定向我们的客户端发出了多次请求,而转发我们的客户端只发出了一次请求。

  3. 重定向的控制权在客户端,转发的控制权在服务端。

代码分析

接下来让我们研究一下 RetryAndFollowUpInterceptor 的实现原理,我们看到 RetryAndFollowUpInterceptor.intercept 方法:

可以看到,这里外部通过一个循环,实现不断重定向,可以看一下循环内主要做了什么:

  1. 进行一些预处理
  2. 调用 chain.proceed 方法进行请求获取 Response
  3. 过程中若下层抛出异常,则尝试重定向
  4. 若不满足重定向条件,则抛出异常
  5. 若出现其他未知的异常,则通过抛出异常释放资源
  6. 在本次 Response 中设置上一次的 Response priorResponse,且body为空
  7. 根据 Response 中的 response code 进行重定向,调用 followUpRequest 方法获取重定向后的 request followUp
  8. 若重定向后的 followUp 为 null,说明不再需要重定向,停止 timeout 计时并返回 Response
  9. 若重定向超过指定次数(默认 20 次),则抛出异常。
  10. 若仍未返回,则需要下一次重定向,对下一次的 request 等变量进行赋值。

让我们看看 followUpRequest 方法做了什么:

可以看到,主要是针对重定向的几个状态码进行特殊处理,从中取出 Location 字段,构造重定向后的 request

BridgeInterceptor

BridgeInterceptor 的名字取的非常形象,它就像一座桥梁,连接了用户与服务器。在用户向服务器发送请求时,它会把用户所构建的请求转换为向服务器请求的真正的 Request,而在服务器返回了响应后,它又会将服务器所返回的响应转换为用户所能够使用的 Response

让我们看到 BridgeInterceptor.intercept 方法:

可以看到,这里主要对 Header 进行处理,将一些原来 request 中的 Header 进行处理后设置进了新 request,并用其进行请求。其中若调用者未设置 Accept-Encoding,则它会默认设置 gzip

而在对 response 处理时,若之前设置了 gzip,则进行 gzip 解压。这种自动解压会自动将 Content-LengthContent-Encoding 字段从 Header 中移除,因此上层可能会获取到 -1。

而这里关于 Cookie 的处理我们暂时不关心,后续文章中再对其作介绍。

CacheInterceptor

CacheInterceptor 主要负责了对缓存的读取以及更新,让我们看看其 intercept 方法:

可以看到,这里主要是以下步骤

  1. 尝试从缓存中获取了缓存的 response
  2. 根据 当前时间、request、缓存的response 构建缓存策略。
  3. 若缓存策略不能使用网络(networkRequest == null),且无缓存(cacheResponse == null),则直接请求失败。
  4. 若缓存策略不能使用网络,由于前面有判断所以可以确定有缓存,直接构建缓存的 response 并返回。
  5. 调用 chain.proceed 网络请求获取 response
  6. 对 code 304 作出处理,结合本地及网络返回数据构建 response 并返回
  7. 构建网络请求的所获得的 response ,并且由于该网络请求并未进行过缓存,进行缓存并返回结果

而关于缓存相关的具体实现这里先不过多做介绍,后面会专门开一篇文章进行分析,这里主要以流程为主。

ConnectInterceptor

ConnectInterceptor 主要负责的是与服务器的连接的建立,它的代码非常短:

这里主要是调用 transmitter.newExchange 构建一个 Exchange,之后调用了 realChain.proceed(request, transmitter, exchange) 方法。

这个 Exchange 类究竟是什么呢?我们看到它的 JavaDoc:

Transmits a single HTTP request and a response pair. This layers connection management and events on {@link ExchangeCodec}, which handles the actual I/O.

也就是说 Exchange 类可以将 ExchangeCodec 这个类的连接管理及事件进行分层,而 ExchangeCodec 是一个真正执行 I/O 的类,看来这个类主要是进行一些连接管理的事务。在 newExchange 的过程中可能就创建/复用了客户与服务器的连接。

这里具体的连接获取过程我们暂时先不做介绍,在后续文章中会详细进行介绍,此篇文章更偏向整体流程的讲解。

CallServerInterceptor

CallServerInterceptor 是整个网络请求链的最后一个拦截器,它真正实现了对服务器 Response 的读取,让我们看看它的实现:

这里代码量非常多,但其实核心是下面几步:

  1. 写入Request Header
  2. 写入Request Body
  3. 读取Response Header
  4. 读取Response Body

其具体实现我们后续文章再进行介绍,到了这里整个责任链的大体流程我们就分析完了。

参考资料

HTTP状态码

OkHttp之旅系列


Android Developer in GDUT