桥接拦截器与缓存拦截器
本文概述:
- 本文探究了OkHttp 中的桥接拦截器与缓存拦截器;在桥接拦截器中介绍了其工作职责、工作流程,以及两个源码细节(拿不到的响应体长度、Cookie 的处理);在缓存拦截器中介绍了Http 缓存策略及执行逻辑,深入源码讨论了OkHttp 是如何判断缓存可用;
桥接拦截器:
职责:帮助我们去补全请求头(例如,在OkHttp 中不必须要手动配置Host )
-
将用户的请求对象作为参数,实例化新的Request 对象,并为其补全请求头后,将这个新的请求对象交给下一个拦截器处理并得到响应结果;
-
拿到响应结果后:
- 保存Cookie:如果有,就保存Cookie,回调之前配置OkHttpClient 整的CookieJar 中的saveFromCookie;
- 解析GZIP:如果服务端使用了GZIP 压缩数据,那么此时就会进行解压
- 最后将处理好的response 返回给上一个拦截器
-
示意图:
-
源码细节一:如果拿不到的响应体长度 ---> 使用分块编码(Http 协议的东西)
if (contentLength != -1L) { requestBuilder.header("Content-Length", contentLength.toString()) requestBuilder.removeHeader("Transfer-Encoding") } -
源码细节二:Cookie 的处理
//如果说,在配置OkHttpClient 设置了CookieJar(这是一个接口,内部两个方法,保存Cookie以及加载Cookie),那么在桥接拦截器中就会回调这个加载Cookie 的方法,将这个Cookie 设置到请求头Cookie 中去; val cookies = cookieJar.loadForRequest(userRequest.url) if (cookies.isNotEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies)) }
缓存拦截器:
Http 的缓存是怎么玩的?
-
强缓存:浏览器不会向服务器发送请求而是将本地的缓存发给用户
-
怎么判断是强缓存:根据Http 响应头的字段
-
Expires:缓存过期时间
- 如果当前时间小于缓存过期时间 ---> 将本地缓存返回给用户
-
Cache-Controller:是否使用强缓存的字段,有并且符合条件就使用强缓存
-
max - age:秒,资源最大有效时间
-
immutable:响应资源不会变
- 有这个字段,直接使用强缓存
-
min-fresh:请求缓存最小新鲜度(用户任务这个缓存)
-
no - cache:不使用缓存
-
no - store:不允许资源被缓存
-
-
-
协商缓存:浏览器会向服务端发请求,根据服务端响应头字段判断是否命中协商缓存,命中返回304,客户端使用协商缓存返回给用户;没有命中,服务端回传数据,不使用缓存;
-
服务端响应304 :此时是没有响应体的
- 告诉客户端,去使用本地缓存
-
协商缓存的响应头字段:举例说明第一套
- 客户端的请求字段中包含If - Modified (这是一个时间),服务端拿到这个时候,判断自己在指定的时间内有没有修改资源;如果没有,服务端返回304(没有响应体 ---> 让客户端去使用本地的协商缓存);如果修改了,服务端正常返回响应体;
- 注意服务端的响应头中会有一个字段:Last - Modified (代表资源最后的修改时间)
- 这两个是配套使用的将Last - Modified 拿给If - Modified
-
协商缓存的响应头字段:举例说明第二套
- 服务端响应头包含字段ETag (表示资源在服务端的唯一标识)
- 客户端请求体包含字段If-None - Match (服务端拿到这个东西跟ETag 值进行比较,如果匹配,就返回304 ---> 客户端使用协商缓存)
-
深入探究OkHttp 缓存拦截器
-
怎么实现的:将缓存的判定封装在缓存策略中,通过compute 方法判断是否可以使用缓存
-
这个缓存指的是强缓存,协商缓存是需要去请求服务器的(这个是拿给下一个拦截器处理的)
-
源码分析:不仅实现了Http 缓存,自己还搞了自己的
-
拿到缓存策略对象:调用compute 方法
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute() -
对类属性进行赋值:
-
业务逻辑:
val networkRequest = strategy.networkRequest val cacheResponse = strategy.cacheResponse-
什么情况下会两个都为空?
-
如果说请求头携带字段XXX (代表强制使用缓存),但是此时没有缓存或者缓存已经失效了,那么就会两个都为空;此时OkHttp 就会在本地创建一个Response 并且将响应码置为504
-
指定本次请求一定要使用缓存
-
- 当两个都不为空时:使用协商缓存
-
-
-
OkHttp 是怎么判断缓存可用的?
-
首选判断有没有缓存:
-
有:继续向下
-
没有:判断是否指定了必须使用缓存
Cache-Control:only-if-cached- 是:OkHttp 响应504
- 没有:请求服务端(不能使用缓存都会走到这里)
-
-
缓存存在:判断是否为Https 请求 :
- 是:在缓存的响应中包含握手信息(咩有握手信息这个缓存不能用)
-
判断缓存响应码与响应头:
-
为200、203、204、300、301、404、405、410、414、501、308、302 中的一个
- 如果是临时重定向:需要包含一些特殊的字段
-
并且:请求头和缓存的响应头不包含Cache - Control : no-store
- 意味着缓存能用 ---> 那么继续向下走
-
-
判断用户请求配置:
-
请求头中没有设置Cache - Control : no-cache (客户端说明了,我不适用缓存),没有设置only-if-cached (表示这次用的是协商缓存),没有设置If - Modified (表示这次用的是协商缓存)
- 表示我还是希望直接使用缓存 (强缓存)
-
-
如果说响应资源不变:
-
immutable:响应资源不会变
- 有这个字段,可以直接使用强缓存
- 没有:那么去判断资源是否失效(缓存存活时间 < 缓存有效时间)
-
-