OkHttp源码分析系列(下)拦截器篇

引言

在上一篇文章中,讲了一下OkHttp触发一个网络请求调用顺序的一个大概的流程,本篇文章将重点讲述一下OkHttp中各个拦截器的作用。篇幅比较长,提前预警一下。 有兴趣的可以看一下上一个系列,OkHttp源码分析系列(上)流程篇。

概述

先来看一下添加拦截器的位置的代码: image.png 可以看到,这里是一个ArrayList添加了5个具体的拦截器和两个拦截器List,分别为client.interceptors()和client.networkInterceptors()。接下来将按照添加的顺序讲一下各个拦截器的作用。

自定义拦截器

首先,在我们的网络请求创建OkHttpClient的时候,调用add方法,添加一个自定义的拦截器。下面是添加自定义拦截器的代码,及运行后的Log截图。 image.png image.png 接下来我们看一下自定义拦截器的执行顺序,可以看到在getResponseWithInterceptorChain()方法中,最先添加的就是自定义的拦截器,然后我们再Debug看一下这个拦截器的List,可以通过下图看到,我们在MainActivity中的自定义拦截器,确实是List中的第一个,所以执行的时候也是先执行自定义拦截器,然后再执行OkHttp默认添加的拦截器。 image.png 可以在普通的自定义拦截器中,可以封装一些公共参数,请求头,数据加密等操作。

RetryAndFollowUpInterceptor拦截器

重试和重定向拦截器核心代码如下: image.png image.png 第一部分其实就是一个拦截器的调用,第二部分为重定向的一个方法,这个方法内部其实是一些我们常见的错误码的一些对应处理。 image.png image.png 重定向的最大次数限制为20次,源码中是这么写的。

Chrome follows 21 redirects; Firefox, curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.

可以看出这个拦截器的主要功能为:重试和重定向操作。

BridgeInterceptor

BridgeInterceptor,顾名思义,桥梁拦截器。这个桥梁是谁和谁的呢?是应用层和数据层直接的一个拦截器。他的功能主要有两个,第一个是请求前,第二个是请求后。接下来看一下源码: image.png image.png 第一张图片为:请求前的,可以看到这里做个了一些 Cookie,User-Agent,Host,Content-Length等的参数设置,这个是所有请求都要设置的一些参数,和HTTP协议有关。 第二张图片为:通过proceed方法获取的请求后的参数,对请求后的结果,存储Cookie和gzip解压。 这个拦截器的主要功能为:应用层和数据层之间的数据转换,添加固定的协议参数。

CacheInterceptor

缓存拦截器,功能就是读/写缓存。在请求前获取是否有缓存。请求后缓存请求结果。 image.png 缓存拦截器触发的时候,会去判断一下cache是否为空,这个cache是okhttpclient初始化的时候传入的,需要注意的是:默认的OkhttpClient是不初始化这个参数的。所以缓存拦截器这里的cache为null。所以CacheStrategy对象的response为null,request不为null。那么最终还是会去执行的chain.proceed方法。 image.png 如果cache对象不为null会是什么流程呢? cache对象不为null的时候会执行get方法:cache.get(chain.request()),这个cache内部其实是一个DiskLruCache的get和put操作。需要注意的是cache缓存只缓存GET请求。具体看如下代码。 image.png 除此以外,源码中还有这样的一句注释,有兴趣的同学自行查看。

Don't cache non-GET responses. We're technically allowed to cache HEAD requests and some POST requests, but the complexity of doing so is high and the benefit is low.

继续接着讲,如果请求的时候对应的request的cache中的response不为null,那么在请求结束以后,会去取一下服务器的返回结果码,如果为304则代表数据没有修改,则会使用缓存结果。 image.png

ConnectInterceptor

连接拦截器乍一看代码非常简单,根本没几行代码。实际不是这样的,核心代码在下面的红框中,我们看一下具体内部实现。 image.png 先看一下这个类的构造方法StreamAllocation: image.png 这个类初始化在重试和重定向拦截器,有兴趣的同学可以去翻源码看一下,这几个参数都是在那边设置的。这里具体讲一下第一个连接池。连接池是在创建client对象的时候初始化了一个默认的连接池。 image.png 最大闲置连接数量为5个,最大时间闲置时间为5分钟。 这个连接池内部有一个RealConnection的双端队列,connectionpool的put/get操作,都是针对这个队列的操作。 这个类基本就是连接拦截器的核心了,比较关键。我们继续往下看。 image.png RealConnection实现了Connection接口,看一下这个接口的代码。Route,Socket,Handshake,Protocol。 这不就是路由,Socket连接,握手,协议接口。 image.png 接下来我们回到拦截器这里,继续看StreamAllocation,调用了他的newStream方法,我们看一下。 image.png 继续看一下findHealthyConnection方法: image.png 这里是一个while true的循环方法,继续往下看。这个方法的代码比较多,我就不贴出来了,可以看一下核心代码: image.png image.png 这些方法都是在RealConnection里面的,主要是发起Socket请求和协议相关。 最后需要注意的是连接连接器调用的proceed方法和其他拦截器的调用方法都不一样。其他的是调用的一个参数的方法,这里调用的是4个参数的。 image.png

CallServerInterceptor

最后一个拦截器了。首先看到一行注释:

This is the last interceptor in the chain. It makes a network call to the server

这基本就概述了这个拦截器的作用了。主要通过HttpCodec接口,根据Http协议的版本有对应的codec,由对应的codec来实现请求结果的获取。生成最后的Response。 image.png

结束语

OkHttp两篇文章写完,就基本讲的差不多了。源码看着也比较复杂头痛,看着看着就不知道看到哪里去了。。下个系列还在准备中,下篇文章再见。

转载请注明出处,微信公众号的转载请联系本人授权。

猜你喜欢

转载自juejin.im/post/7105788146855444510