okhttp详解系列四:缓存拦截器
- okhttp详解系列一:开篇
- okhttp详解系列二:重试重定向拦截器
- okhttp详解系列三:桥拦截器 BridgeInterceptor
- okhttp详解系列四:缓存拦截器
- okhttp详解系列五:连接拦截器 ConnectInterceptor
- okhttp详解系列六:服务请求拦截器 CallServerInterceptor
缓存使用方法
1 | private val client: OkHttpClient = OkHttpClient.Builder() |
缓存机制只实现了get请求的缓存,不支持其他的请求类型,比如POST。下面的官方的说明:
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.
Cache-Control请求头是控制缓存策略的关键,server、client端都可以进行设置。Cache-Control决定了哪些response可以被缓存,以及缓存的response是否满足当前的request。协议定义可以参考RFC 7234, 5.2。
缓存拦截器流程
缓存拦截器的处理流程如下:
- 首先通过url的md5值去读取本地可用缓存(后面会校验缓存是否可用);
- 计算得到CacheStrategy,实际上就是计算得到networkRequest和cacheResponse,如果两者都为null,就直接报504错误;如果networkRequest是null,表示直接使用缓存。
CacheStrategy
CacheStrategy的生成使用的条件比较多,比如request CacheControl、response CacheControl、条件请求等,这块就不详细介绍了。其中要计算响应的年龄和响应的保鲜期,下面详细介绍两者的计算方法。
响应年龄的计算方法
响应的年龄是自它生成(或者通过源服务器成功验证)之后所经过的时长,计算方法如下:
- 第一步:计算原始响应时长,”接收到响应的时间” 减去 “响应的服务时间”(表示生成该response的时间,响应头中的Date字段,如果没有,则取接收到响应的时间),得到的时间差与Age字段进行比较,取最大值;
- 第二步:计算响应时长,“接收到响应的时间”减去“发送响应的时间”;
- 第三步:计算本地年龄(被缓存的response在本地的保存时长),当前时间减去接收到response的时间;
- 最后,把上述三者加和就得到了响应的最终年龄;
响应头的Age字段有必要解释一下:如果存在Age字段,则表示这个response是基于缓存生成的,来自缓存服务器,而不是来自原始服务器。所以Age表示该次响应到源服务器提供服务的时间差。
响应保鲜期的计算方法
一个被缓存的response,如果超过了保鲜期,就表示这个被缓存的response必须再次通过源服务器的验证后才能继续使用;还在保鲜期内的response就可以不经过源服务器的验证就能使用。
- 如果response Cache-Control中指定了max-age(单位秒),保鲜期就取max-age的值。
- 如果没有max-age,被缓存响应中指定了Expires(时钟时间),则保鲜期就是Expires减去响应服务时间(响应头中的Date字段,如果没有则取接收到响应的时间)后得到时间差。
- 如果Expires也没有,被缓存响应中存在Last-Modified响应头,则保鲜期就是服务时间(如果没有就使用请求发送的时间)减去Last-Modified的时间差的**10%**。
- 如果上述信息都没有就默认是0。
- 最后,上述计算得到的保鲜期与request Cache-Control的max-age的值进行比较,取小值。
Request Cache-Control中还有两个字段会影响保鲜期的时长:
- max-stale 是client端额外给保鲜期增加的时长,就像有的人比较节省,买的东西刚过了保鲜期没几天,就认为还可以继续用,再多用几天。
- min-fresh 是client端给保鲜期减掉的时长,就好比有的人比较讲究,买的食品马上就到保质期了(实际还差几天)就不想吃了。
所以,最终计算的保鲜期还要加上max-stale,再减去min-fresh。
写入Cache
这块逻辑相对比较简单。
1 | internal fun put(response: Response): CacheRequest? { |