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简单分析的更多相关文章

  1. OkHttp之ConnectInterceptor简单分析

    在< Okhttp之CacheInterceptor简单分析 >这篇博客中简单的分析了下缓存拦截器的工作原理,通过此博客我们知道在执行完CacheInterceptor之后会执行下一个浏览 ...

  2. Okhttp之CallServerInterceptor简单分析

    在Okhttp源码分析专栏的几篇博客分析了Okhttp几个拦截器的主要功能,还剩下最后一个拦截器CallServerInterceptor没有分析,本篇博客就简单分析下该拦截器的功能. 在Okhttp ...

  3. OkHttp之BridgeInterceptor简单分析

    在< Okhttp源码简单解析(一) >这篇博客简单分析了Okhttp请求的执行流程,通过该篇博客我们知道OkHttp的核心网络请求中内置"拦截器"发挥了重大作用:本篇 ...

  4. Okhttp之连接池ConnectionPool简单分析(一)

    开篇声明:由于本篇博文用到的一些观点或者结论在之前的博文中都已经分析过,所以本篇博文直接拿来用,建议读此博文的Monkey们按照下面的顺序读一下博主以下博文,以便于对此篇博文的理解: <Okht ...

  5. Okhttp对http2的支持简单分析

    在< Okhttp之RealConnection建立链接简单分析>一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开t ...

  6. Okhttp之RealConnection建立链接简单分析

    在之前的博客中我们知道Okhttp在发起链接请求先从链接池中获取连接,如果链接池中没有链接则创建新的链接RealConnection对象,然后执行其connet方法打开SOCKET链接(详见< ...

  7. Okhttp之RouteSelector简单解析

    继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入 ...

  8. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  9. CSipSimple 简单分析

    简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...

随机推荐

  1. ECU

    ECU(Electronic Control Unit)电子控制单元,又称“行车电脑”.“车载电脑”等.从用途上讲则是汽车专用微机控制器,也叫汽车专用单片机.它和普通的单片机一样,由微处理器(CPU) ...

  2. android与linux之间的关系

    篇一(system/core/init/init.c): 对Android感兴趣的朋友都知道,Android系统是建立在Linux内核之上的.那么Linux内核和Android什么关系?Linux内核 ...

  3. C++ 单词接龙

    问题描述: 拉姆刚刚开始学习英文字母,对单词排序很感兴趣,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一个单词的尾字母相同,力能编写一个计算机程序帮助拉姆进行判断 ...

  4. Servlet3.0异步请求

    在Servlet3.0之前,Servlet采用Thread-Per-Request的方式处理请求 即每次Http请求都有一个线程从头到尾负责处理 如果一个请求需要进行IO操作,比如访问数据库.调用第三 ...

  5. 高并发下,php与redis实现的抢购、秒杀功能

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...

  6. Spring MVC 实践笔记

    1.了解 Maven 的用法:http://spring.io/guides/gs/maven/ .这篇英文非常详细的演示了 Maven 的用法,在命令行下执行.注意,运行Maven的时候,Maven ...

  7. photoshop cc下载与安装

    地址:https://jingyan.baidu.com/article/c275f6bacdd927e33d756729.html

  8. bootstrap自定义——栅格列数修改

    从下载的bootstrap文件中找到less文件夹里面的variables.less,然后可以找到栅格列数进行修改 然后执行一下bootstrap.less,通过命令行,切换到其所在的目录D:\03 ...

  9. CSS3:边框属性

    前言 学习这些CSS属性并不是要自己要设计多好看的样式,而是看到网上的代码能看得懂. 效果 本章将围绕如下效果进行解释: border border-width: 边框宽度. border-style ...

  10. 什么是JSON?

    JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度. JSON就是一串字符串 只不过元素会使用特定的符号标注. {} 双 ...