源码分析
Serves requests from the cache and writes responses to the cache
为来自缓存的请求提供服务,并将响应写入缓存
我们可以看到在proceed方法之前有一个get方法,在proceed方法之后有一个put方法。 我们在之前文章(juejin.cn/post/700509… 讲到,拦截器用到了责任链的设计模式,在proceed方法之前是对请求进行处理,在proceed方法之后是对响应进行处理,这里的逻辑大致是先用get方法查看是否有缓存;拿到响应后用put进行缓存。
先来看put方法,先对此响应的请求方法进行对比如果是post||patch||put||delete||move其中一个,则删除该缓存,并且return。 接着是如果不是GET请求则直接return 来看官方注释:
//不缓存非get响应。我们在技术上允许缓存HEAD请求
// POST请求,但这样做的复杂性很高,而好处很低。
接着是hasVaryAll方法,如果Vary标头包含星号则返回true。这样的响应不能被缓存。
接下来是实现缓存的方法,使用DiskLruCache方法缓存(缓存的方式是磁盘缓存,而缓存的算法是 LruCache),key为request的url进行md5(),hex()计算得来。
看完put方法后再看get方法就很清晰了,只有是GET请求的时候,缓存池里面才会有。
接着看get后面的代码,这个Factory传入当前时间,请求,以取出的缓存。
来看一下acheStrategy 的构建函数,三段注释分别为:
1.给定一个请求和缓存的响应,它将确定是使用网络、缓存还是两者都使用。 选择缓存策略可能会向请求添加条件(如有条件的get的“if - modified - since”头)或向缓存的响应添加警告(如果缓存的数据可能过时)。
2.在网络上发送的请求,如果调用不使用网络则为空。
3.要返回或验证的缓存响应;如果调用不使用缓存,则为空。
从注释的翻译可以看出,networkRequest 对象如果不等于空, 那么代表需要请求一次网络,如果等于空则不需要。而 cacheResponse 对象如果不等于空,那么代表这个缓存是可复用的,如果等于空则不能复用
从缓存的 Response 中取一些响应头,这些响应头的作用都是同一个,用于判断这个缓存的 Response 是否处于可复用的状态。
再来看compute方法,使用cacheResponse返回满足请求的策略。
看到computeCandidate方法
如果缓存的响应缺少所需的握手,则删除它。
如果这个是一个 Https 的请求,必须要经过身份认证才可以使用这个缓存
如果服务器没有明确指明禁用缓存,并且缓存没有过期的话,那么就直接复用已缓存的 Response 对象
//查找一个条件添加到请求。如果条件满足,则响应体将不会被传输。
客户端有缓存但是已经过期了,那么这个时候需要将我们之前缓存过的 Response 资源标识取出来,并把这个标识添加进请求头,然后重新请求一次服务器,让后台决定要不要复用这个已过期的缓存。
缓存的使用讲完了,再回到缓存拦截器。
如果我们被禁止使用网络和缓存不足,失败。
如果不需要请求网络来判断缓存是否可用,那么直接返回这个缓存的 Response 对象,否则就获取网络上面的数据
//如果我们也有缓存响应,那我们就是在做条件get。
如果本地缓存过期了,那么我们会请求网络来询问服务器是否复用这个缓存,如果服务器返回响应码 304,那么就证明服务器允许了,但是服务器会给我们返回一个空 Body 的 Response 对象,所以我们需要对 networkResponse 中的 Body 替换成 cacheResponse 中的 Body,然后刷新本次请求的缓存,最后再将结果返回回去。
刚刚那种情况是本地有缓存的情况下,那么我们再看看本地在没有任何缓存的情况下 OkHttp 会如何处理。
还是常规操作,如果这个响应头有 Body 并且满足缓存的条件就对它进行缓存,但是如果请求的方式不符合缓存要求,那么也会将它从缓存中移除,最后将 Response 对象返回回去
源码总结
希望大家看看参考文献,这一部分理解不够深入,基本上都是抄袭轮子哥的,望见谅。
这个拦截器主要作用就是处理请求缓存,首先会根据 url 到磁盘中读取相应的缓存,还有一点需要注意,目前 OkHttp 不支持读取 Post 请求的缓存,如果读取到了缓存,还会判断这个缓存是否可用,前提条件是客户端和服务器都不能禁用缓存,否则将无法使用缓存功能,如果缓存过期了,那么 OkHttp 需要再发一次请求确定缓存是否可以复用,如果服务器返回 304 则代表缓存是可用的,但是 Body 是空的,需要对当前的 Response 对象和已缓存的 Response 对象结合成一个新的 Response 对象,最后再将新的 Response 对象更新到缓存中,如果在此之前没有任何缓存,那么就先判断是否满足缓存的条件,如果满足的话就直接写入到缓存中。