🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# OkHttp 缓存分析 在 OkHttp 中使用缓存很方便,OkHttp 内部已经定义好了一套缓存策略,我们在使用的时候,只用在构建 OkHttpClient 的时候指定缓存的的位置和大小就 ok 了,具体的在 [OkHttp 源码解析(二)拦截器原理分析](https://mp.weixin.qq.com/s/T5HOZeiI6vH_MAny9F6tTQ)中已经分析过了,这里再对 OkHttp 的缓存模块进行梳理。 先贴一个经典的 http1.1以后的缓存流程图: ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/15834864563737.jpg) 根据图中的信息主要是对一些请求/响应头的判断,这些在 OkHttp 的拦截器中,OkHttp 会进行一定的缓存处理,但是我们也是可以认为的干预,也就是在构建 Request 的时候,进行一些认人为的干预,也就是设置 Request 对象的 cacheControl 属性。比如: ```java Request request = new Request.Builder() .url("https://www.qq.com") .cacheControl(new CacheControl.Builder().noCache().build()) .build(); ``` 在 OkHttp 的缓存拦截器中会对这里设置的 cacheControl 进行判断,优先级是在上面图中的一些请求/响应头的前面判断的。 下面先来介绍下一些常见的关于缓存的请求头/响应头 ## 一些关于缓存的请求头/响应头 ### Expires Expires 表示服务端返回的缓存失效时间。下一次请求时,请求时间小于 Expires 的值,直接使用缓存数据。 由于到期时间是服务端生成,客户端和服务端的时间可能存在误差,导致缓存命中的误差 ### Cache-Control Http1.1 中采用了 Cache-Control 代替了 Expires,常见 Cache-Control 的取值有: * private 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存) * public 所有内容都将被缓存(客户端和代理服务器都可缓存) * max-age=xxx 缓存的内容将在 xx 秒后失效,这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高 * no-cache 必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。 需要使用对比缓来验证数据(并不是字面上的不使用缓存) * no-store 所有内容都不缓存。 - **不会触发** 强制缓存,对比缓存 ### Last-Modified 和 If-Modified-since * Last-Modified 服务器返回的 缓存控制字段 * If-Modified-since 如果支持缓存 ,在进行 http 请求的时候,If-Modified-since 会使用上次请求返回的 last-modified值,作为客户端向服务端请求的时的请求头, ### ETag 和 If-None-Match * ETag 是服务器返回响应的唯一标识 * if-none-match 如果支持缓存 ,在进行 http 请求的时候,if-none-match 会使用上次请求返回的 etag 值,作为客户端向客户端请求时的请求头。 ## OkHttp 缓存处理流程图 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/OkHttp缓存机制.png) 大体上的流程如下: 1. 如果用户设置了缓存,先拿到上次请求的**候选缓存** 2. 根据**候选缓存**获取OkHttp 的缓存策略 3. 如果**候选缓存为空**,则返回请求request不为空,**不使用缓存**,**使用网络请求** 4. 如果是 https 并且握手信息为空,**不使用缓存,使用网络请求** 5. 如果判断请求和候选缓存的cache-control 字段是否是 no-store,如果是,**不使用缓存,使用网络请求** 6. 判断request 的cacheControl 属性是否设置了不支持缓存或者本次请求中的是否包含了 if-modified-since或者 if-none-match请求头,如果不支持或者有两个请求头中的任何一个,**不使用缓存,使用网络请求** 7. 判断候选缓存的 cacheControl,如果候选缓存中的 cacheControl 支持缓存或者缓存未过期,**使用缓存,不请求网络** 8. 根据候选缓存中的 etag、last-modified、Date请求头字段,分别设置本次请求 Request 的if-none-match、if-modified-since、if-modified-since字段,以此往后,只要有一个,后面的请求头不会被设置,这时返回本次**请求 networkRequest 和 缓存 cacheResponse 都不为空。** 9. 经过上面已经拿到了 OkHttp 中的缓存策略。此时如果请求为空,候选缓存为空,**则构建 Response,抛出 504(网关超时)错误。return 结束** 10. 如果本次请求为null,**使用本地缓存(走到这里,缓存一定不为空)return 结束** 11. 这里表示networkRequest 和 缓存 cacheResponse 都不为空,那么代表要请求网络,继续请求下个拦截去,然后拿到下个拦截器返回的 response 为 netwoekResponse。 12. 如果返回的 networkResponse 的**状态码为 304,表示请求的响应未修改,那么继续使用本地缓存,更新本地缓存。 return结束** 13. 如果返回的 networkResponse 有 body并且本次请求和本次网络返回是被允许缓存的,则**更新本地缓存并return结束** 14. 如果本次请求是不是 GET(或者是不支持缓存的请求类型),那么**移除本地对应 url 的请求缓存。return结束。** 整体上就是这么个流程,如果是要了解 OkHttp 的缓存机制,那么必然要先了解通用的缓存原理,再来分析 OkHttp 的缓存原理,要不然只有一脸懵逼的份。 推荐文章: [OKHTTP之缓存配置详解](https://blog.csdn.net/briblue/article/details/52920531) [HTTP 缓存机制一二三](https://zhuanlan.zhihu.com/p/29750583) [彻底弄懂HTTP缓存机制及原理](https://www.cnblogs.com/chenqf/p/6386163.html)