《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 {//如果配置了缓存:优先从缓存中读取ResponseResponse 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禁止使用网络,并且缓存无效,直接返回空的Responseif (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简单分析相关推荐

  1. 你想要的系列:网络请求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析

    Okhttp系列文章: 你想要的系列:网络请求框架OkHttp3全解系列 - (一)OkHttp的基本使用 你想要的系列:网络请求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析 你想 ...

  2. R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集、非线性:基函数展开和样条分析、你简单分析的不重要特征,可能只是线性不显著、而非线性是显著的

    R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集.非线性:基函数展开和样条分析.你简单分析的不重要特征,可能只是线性不显著.而非线性是显著的 目录

  3. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  4. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  5. howdoi 简单分析

    对howdoi的一个简单分析. 曾经看到过下面的这样一段js代码: try{doSth(); } catch (e){ask_url = "https://stackoverflow.com ...

  6. Mac与Phy组成原理的简单分析

    Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fir ...

  7. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码

    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候... 正态分布(Normaldistribution),也称"常态分布",又名高斯分布(Gaussiandi ...

  8. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

  9. Hessian 源码简单分析

    Hessian 源码简单分析 Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可. 服务端: 服务端通常和spring 做集成. 首先写一个接口: public ...

  10. python预测股票价格tushare_用tushare对股票进行简单分析

    用tushare对股票进行简单分析(仅供交流学习) import numpy as np import pandas as pd import matplotlib.pyplot as plt imp ...

最新文章

  1. Alchemy环境的搭建
  2. S3C6410嵌入式应用平台构建(六)——linux-3.14.4移植到OK6410-(Yaffs2文件系统移植)...
  3. hdu 1078(记忆化搜索)
  4. Mysql+Navicat for Mysql
  5. javascript进制转换_《算法笔记》3.5小节——入门模拟-gt;进制转换
  6. 基本线程同步(五)使用Lock同步代码块
  7. 更改npm淘宝源,并设置cnpm
  8. java jar metainf_java – 从生成的jar文件中排除META-INF / maven文件夹
  9. 【笔记】《Web全栈工程师的自我修养》
  10. 直线插补算法---matlab仿真程序
  11. 斐波那契数列(II)
  12. Spring Boot 通过 Mvc 扩展方便进行货币单位转换
  13. 一道受用终身的测试题
  14. 如何把图片转化成excel表格?
  15. libxml2 c库使用
  16. java png生成webp图片_jpg、png格式的图片转换成webp后颜色失真的问题
  17. P1162 填涂颜色问题(广度优先搜索BFS)
  18. uni-app开发经验分享二十二: uni-app大转盘思路解析
  19. 2019-06-17问答系统项目落地调研
  20. Zepto课程-张晓飞-专题视频课程

热门文章

  1. Java进阶:SpringMVC中通过监听器将Spring上下文对象放置到servletContext中,方便其他地方使用
  2. linux运维脚本编写,Linux运维基础技能: 脚本编程与Linux命令
  3. springcloud配置负载均衡 及方式_Springcloud-Ribbon负载均衡NODO
  4. mysql 8.X主从复制
  5. SpringBoot两种定时任务(Spring Schedule 与 Quartz 整合 )实现
  6. 前后端分离开发技术的一些思考
  7. Java配置文件读取写入通用类库:PropUtils 属性文件类
  8. DB2 亲身实例(GUI界面) 加 笔记和注意事项
  9. 求一个特定函数在定义区间上的值是否都为素数
  10. Kaggle实战之leaf classification(树叶分类)