Okhttp之CacheInterceptor简单分析
《OkHttp之BridgeInterceptor简单分析 》简单分析了BridgeInterceptor的工作原理,在Okhttp的拦截器链上BridgeInterceptor的下一个拦截器就是CacheInterceptor,所以本文就对此拦截器的功能做一个简单的梳理。顾名思义,该拦截器的工作跟缓存有关。
正如浏览器在访问网络请求的时候提供缓存功能一样,Okhttp也提供了缓存功能,使用Okhttp的缓存其实跟浏览器的缓存有相似之处;不像使用图片缓存框架那样,如果有缓存图片框架会直接使用缓存中的图片(比如ImageLoader),而再继续网络请求;而浏览器不一样,即使有缓存也会再次发起网络请求,下面就简单说下网络请求缓存的一些基本知识,算是为本文对CacheInterceptor的分析做铺垫。
在服务器响应的时候,在”响应头信息”里面包含了资源的最后修改时间,我们可以通过Last-Modified这个header来获取该时间:
既然我们能拿到资源文件的最后修改时间,那么我们就可以根据这个时间来判断服务器上面的资源是否有修改了!怎么做呢?当我们再次请求服务器的时候,在”请求头信息”会带上次请求获取的Last-Modified时间,该时间在请求头信息”用If-Modified-Since 这个header里面,见下图:
当服务器收到请求后检测到If-Modified-Since这个Header,则与被请求资源的最后修改时间进行比对:
if(服务器资源最后修改时间<=If-Modified-Since) {
说明对应的资源没有修改过,此时服务器返回304状态码,不再需要将报文主体部分返回给客户端;
客户端此时直接用缓存的资源即可
}else {
说明资源又被改动过,服务器返回状态码200,此时就需要将报文的主体部分返给客户端,;
}
其实,通过上面的图我们发现在请求/响应的头信息里面还有两个Header:If-None-Match/Etag。当第一次请求的时候,”响应头信息”里面有一个Etag的Header可以看做是资源的标识符。再次请求的时候,”请求头信息”会包含一个If-None-Match的头信息。此时服务器取得If-None-Match后会和资源的Etag进行比如,如果相同则说明资源没改动过,那么响应304,客户端可以使用缓存;否则返回200,并且将报文的主题返回给客户端(Etag的说明可以参考百度百科)
可以简单的举个例子:
清空浏览器缓存,随便用浏览器发出一个http请求,得到如下图所示的结果:
刷新浏览器再次发起请求的时候,则因为缓存的作用会有如下结果:
到此为止,简单的分析完了浏览器缓存的那些事儿,下面带着上面的一些基本理论来正式分析一下CacheInterceptor的源码,当然仍然是分析其intercept方法:
Response intercept(Chain chain) throws IOException {
//如果配置了缓存:优先从缓存中读取Response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//缓存策略,该策略通过某种规则来判断缓存是否有效
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
。。。。
//如果根据缓存策略strategy禁止使用网络,并且缓存无效,直接返回空的Response
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
。。。
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)//空的body
。。。
.build();
}
//如果根据缓存策略strategy禁止使用网络,且有缓存则直接使用缓存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//需要网络
Response networkResponse = null;
try {//执行下一个拦截器,发起网路请求
networkResponse = chain.proceed(networkRequest);
} finally {
。。。
}
//本地有缓存,
if (cacheResponse != null) {
//并且服务器返回304状态码(说明缓存还没过期或服务器资源没修改)
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//使用缓存数据
Response response = cacheResponse.newBuilder()
。。。
.build();
。。。。
//返回缓存
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//如果网络资源已经修改:使用网络响应返回的最新数据
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//将最新的数据缓存起来
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
。。。。
//返回最新的数据
return response;
}
简单的总结下上面的代码都做了些神马:
1、如果在初始化OkhttpClient的时候配置缓存,则从缓存中取caceResponse
2、将当前请求request和caceResponse 构建一个CacheStrategy对象
3、CacheStrategy这个策略对象将根据相关规则来决定caceResponse和Request是否有效,如果无效则分别将caceResponse和request设置为null
4、经过CacheStrategy的处理(步骤3),如果request和caceResponse都置空,直接返回一个状态码为504,且body为Util.EMPTY_RESPONSE的空Respone对象
5、经过CacheStrategy的处理(步骤3),resquest 为null而cacheResponse不为null,则直接返回cacheResponse对象
6、执行下一个拦截器发起网路请求,
7、如果服务器资源没有过期(状态码304)且存在缓存,则返回缓存
8、将网络返回的最新的资源(networkResponse)缓存到本地,然后返回networkResponse.
根据博客《Okhttp源码解析》得出的拦截器执行图(下图):
CacheInterceptor返回的Response交给拦截器链前面的一个拦截器,即BridgeInterceptor来处理,那么这个拦截器是怎么处理的呢,在《OkHttp之BridgeInterceptor简单分析 》这篇博客的最后有了简单的说明:
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//判断服务器是否支持gzip压缩格式,如果支持则交给kio压缩
if (transparentGzip&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
BridgeInterceptor拿到CacheInterceoptor返回的response之后根据请求头的Content-Encoding类型来决定返回的类型,如果是gzip,则将response经过GzipSource 处理后将其Response的body设置为RealResponseBody对象,否则就简单的返回response。
简单流程如下:
到此本篇博客结束,如有不当之处,欢迎批评指正,共同学习
Okhttp之CacheInterceptor简单分析的更多相关文章
- OkHttp之ConnectInterceptor简单分析
在< Okhttp之CacheInterceptor简单分析 >这篇博客中简单的分析了下缓存拦截器的工作原理,通过此博客我们知道在执行完CacheInterceptor之后会执行下一个浏览 ...
- Okhttp之CallServerInterceptor简单分析
在Okhttp源码分析专栏的几篇博客分析了Okhttp几个拦截器的主要功能,还剩下最后一个拦截器CallServerInterceptor没有分析,本篇博客就简单分析下该拦截器的功能. 在Okhttp ...
- OkHttp之BridgeInterceptor简单分析
在< Okhttp源码简单解析(一) >这篇博客简单分析了Okhttp请求的执行流程,通过该篇博客我们知道OkHttp的核心网络请求中内置"拦截器"发挥了重大作用:本篇 ...
- Okhttp之连接池ConnectionPool简单分析(一)
开篇声明:由于本篇博文用到的一些观点或者结论在之前的博文中都已经分析过,所以本篇博文直接拿来用,建议读此博文的Monkey们按照下面的顺序读一下博主以下博文,以便于对此篇博文的理解: <Okht ...
- Okhttp对http2的支持简单分析
在< Okhttp之RealConnection建立链接简单分析>一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开t ...
- Okhttp之RealConnection建立链接简单分析
在之前的博客中我们知道Okhttp在发起链接请求先从链接池中获取连接,如果链接池中没有链接则创建新的链接RealConnection对象,然后执行其connet方法打开SOCKET链接(详见< ...
- Okhttp之RouteSelector简单解析
继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入 ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...
- CSipSimple 简单分析
简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...
随机推荐
- 20145310《Java程序设计》第5次实验报告
20145310<Java程序设计>第5次实验报告 实验要求 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验内容 根据所学内容,编写代码实现服务器与客户端 掌 ...
- 20145313Java第一次实验
实验内容 1.JVM.JRE.JDK的安装位置与区别: 2.命令行运行javac:java:javac -cp; java -cp: 3.PATH,CLASSPATH,SOURCEPATH的设定方法与 ...
- params
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ch06 ...
- JAVA基础补漏--List
Arraylist 通过对ArrayList的源码的查看,他的底层实现是对数组进行数据的操作,所以他的数据特点同数组. 查询快,因为他的内存区域为一个整块,可直接根据索引进行查询. 增删慢,因为每次增 ...
- 问下大家,chorme里用开发者工具看headers,点network标签然后刷新网页并没有headers选项,怎么破?
问下大家,chorme里用开发者工具看headers,点network标签然后刷新网页并没有headers选项,怎么破? 请教个问题 jmeter在Linux服务器压测,抛出很多错误率 但日志中没看到 ...
- Global.asax详解
在网上找了N多相关的东西总说的不够细,现在终于找到了.可以了解web.cofig和Global.asax之间的关系以及执行的顺序. 在Global.asax.cs文件中 protected void ...
- Microsoft's OWIN implementation, the Katana project
参考: https://github.com/aspnet/AspNetKatana/ https://github.com/aspnet/AspNetKatana/wiki/Roadmap
- mysql数据库建表
主键的问题: 简单的id递加.不过在实习的时候,因为id递增的问题,导致一个项目最后好像产生了严重的问题.所以递增适合小型项目,对我们的项目来说90%足够可以用的. 一些限制: 一般就非空就行,唯一的 ...
- maven 引入jar包
问题描述:自己的项目需要引入jar包,已知jar包名字,怎么在maven中添加依赖,使其能自动导入? 第一次使用:本文作为记录! 首先,找到maven仓库的网址!如下: http://mvnrepos ...
- Android----- MD5加密(登录注册得到与IOS相同的加密值,并且解决两个平台汉字加密不相同问题)
最近开发项目中遇到一个这样的问题,注册和登录时需要对信息MD5加密生成一个Token传给后台, 后台会对信息进行比较加密是否相同,才表示你登录或者注册成功,所以,IOS和Android两个平台的tok ...