CDN框架Code

OKHttp源码解析(六)--中阶之缓存基础

2017-06-04  本文已影响1470人  隔壁老李头

前面一节课主要讲解了interceptor及其调用链,本篇这主要讲解http的缓存处理,大体流程如下:

1.什么是缓存
2.为什么要用缓存
3.HTTP缓存机制
4.CacheControl类详解
5.CacheStrategy类详解
6.CacheInterceptor类详解

一、Cache缓存的简介

缓存,顾名思义,也就是方便用户快速获取值的一种存储方式。小到CPU同频的昂贵的缓存颗粒,内缓存,硬盘,网络,CDN反缓存,DNS递归查询,OS页面置换,Redis数据库,都可以看作缓存。它有如下的特点:

PS:在OKHTTP中,使用FileSystem作为缓存载体(磁盘相对于网络缓存),使用LRU作为页面置换算法(封装了LinkedHashMap)。

HTTP作为客户端与服务器沟通的重要协议,对从事android开发的同学来说是一个非常重要的环节,其中网络层优化又是重中之重。今天主要是讲解OKHTTP中的缓存处理,那么首先先简单介绍下为什么要用缓存

二、为什么要用缓存

  缓存对移动端非常重要,使用缓存可以提高用户体验,用缓存的主要在于:

三、HTTP缓存机制

1、HTTP报文

HTTP报文就是客户端和服务器之间通信时发送及其响应的数据块。客户端向服务器请求数据,发送请求(request)报文;服务器向客户端下发返回数据,返回响应(response)报文,报文信息主要分为两部分。

2、缓存分类

(1)按照"端“”分类

缓存可以分为

(2) 按照"是否想服务器发起请求,进行对比"分类

可以分为:

已存在缓存数据时,仅基于强制缓存,请求数据流程如下:

强制缓存.png

已存在缓存数据时,仅基于对比缓存,请求数据的流程如下:


对比缓存.png

我们可以看到两类缓存规则的不同,强制缓存如果生效,则不再和服务器交互了,而对比缓存不惯是否生效,都需要和服务器发生交互。
通过上面了解到,在缓存数据未失效的情况下,可以直接使用缓存数据,那么客户端是怎么判断数据是否失效的?同理,什么时候采用强制缓存,而什么时候又采用对比缓存,这里面客户端是怎么和服务器进行交互的?上面也说道,缓存规则是包含在响应header里面的。莫非所有的交互在header里面?

3、请求头header中有关缓存的设置

3.1 expires

在HTTP/1.0中expires的值围服务器端返回的到期时间,即下一次请求时,请求时间小于服务器返回的到期时间,直接使用缓存数据,这里面有个问题,由于到期时间是服务器生成的,但是客户端的时间可能和服务器有误差,所以这就会导致误差,所以到了HTTP1.1基本上不适用expires了,使用Cache-Control替代了expires

3.2 Cache-Control

Cache-Control 是最重要的规则。常见的取值有private、public
、no-cache、max-age、no-store、默认是private。

响应头部 意义
Cache-Control:public 响应被公有缓存,移动端无用

响应头部 意义
Cache-Control:public 响应被共有缓存,移动端无用
Cache-Control:private 响应被私有缓存,移动端无用
Cache-Control:no-cache 不缓存
Cache-Control:no-store 不缓存
Cache-Control:max-age=60 60秒之后缓存过期

(PS:在浏览器里面,private 表示客户端可以缓存,public表示客户端和服务器都可以缓存)
举个例子。入下图:


缓存header.png

图中Cache-Control仅指定了max-age所以默认是private。缓存时间是31536000,也就是说365内的再次请求这条数据,都会直接获取缓存数据库中的数据,直接使用。

3.3 Last-Modified/If-Modified-Since

上面提到了对比缓存,顾名思义,需要进行比较判断是否可以使用缓存,客户端第一次发起请求时,服务器会将缓存标志和数据一起返回给客户端,客户端当二者缓存至缓存数据库中。再次其你去数据时,客户端将备份的缓存标志发送给服务器,服务器根据标志来进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。
上面说到了对比缓存的流程,那么具体又是怎么实现的那?

Last-Modified

是通过Last-Modified/If-Modified-Since来实现的,服务器在响应请求时,告诉浏览器资源的最后修改时间。

Last-Modified.png
If-Modified-Since

再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回最远的最后修改时间。服务器收到请求后发现有If-Modified-Since则与被请求资源的最后修改时间进行对比。若资源的最后修改时间大于If-Modified-Since,说明资源又被改动过,则响应整个内容,返回状态码是200.如果资源的最后修改时间小于或者等于If-Modified-Since,说明资源没有修改,则响应状态码为304,告诉客户端继续使用cache.

If-Modified-Since.png
3.4 ETag/If-None-Match(优先级高于Last-Modified/If-Modified-Since)

Etag:
服务响应请求时,告诉客户端当前资源在服务器的唯一标识(生成规则由服务器决定)


Etag.png

If-None-Match:
再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识。服务器收到请求后发现有头部If-None-Match则与被请求的资源的唯一标识进行对比,不同则说明资源被改过,则响应整个内容,返回状态码是200,相同则说明资源没有被改动过,则响应状态码304,告知客户端可以使用缓存


If-None-Match.png

正式使用时按需求也许只包含其中部分字段,客户端要根据这些信息存储这次请求信息,然后在客户端发起的时间内检查缓存,遵循下面的步骤

缓存.png

四.Cache-Control类详解

CacheControl 对应HTTP里面的CacheControl

public final class CacheControl {

  private final boolean noCache;
  private final boolean noStore;
  private final int maxAgeSeconds;
  private final int sMaxAgeSeconds;
  private final boolean isPrivate;
  private final boolean isPublic;
  private final boolean mustRevalidate;
  private final int maxStaleSeconds;
  private final int minFreshSeconds;
  private final boolean onlyIfCached;
  private final boolean noTransform;

  /**
   * Cache control request directives that require network validation of responses. Note that such
   * requests may be assisted by the cache via conditional GET requests.
   */
  public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();

  /**
   * Cache control request directives that uses the cache only, even if the cached response is
   * stale. If the response isn't available in the cache or requires server validation, the call
   * will fail with a {@code 504 Unsatisfiable Request}.
   */
  public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();
}

CacheControl类是对HTTP的Cache-Control头部的描述。CacheControl没有公共的构造方法,内部通过一个Build进行设置值,获取值可以通过CacheControl对象进行获取。
Builder具体有如下设置方法:

CacheControl类中还有其他方法,这里就不一一介绍了。想了解的可以去API文档查看。
对于常用的缓存控制,CacheControl中提供了两个常用的于修饰请求,FORCE_CACHE表示只使用缓存中的响应,哪怕这个缓存过期了,FORCE_NETWORK这个表示只能使用网络响应

五、CacheStrategy类详解

CacheStrategy 缓存策略类
OKHTTP使用了CacheStrategy实现了上面的流程图,它根据之前缓存的结果与当前将要发送Request的header进行策略,并得出是否进行请求的结果。

(一)、策略原理

根据输出的networkRequest和cacheResponse的值是否为null给出不同的策略,如下:

networkRequest cacheResponse result 结果
null null only-if-cached (表明不进行网络请求,且缓存不存在或者过期,一定会返回503错误)
null non-null 不进行网络请求,直接返回缓存,不请求网络
non-null null 需要进行网络请求,而且缓存不存在或者过去,直接访问网络
non-null non-null Header中包含ETag/Last-Modified标签,需要在满足条件下请求,还是需要访问网络

以上是对networkRequest/cacheResponse进行的switch查询得出的,下面我们就详细讲解下

(二)、CacheStrategy类的构造

CacheStrategy使用Factory模式进行构造,通过Factory的get()方法获取CacheStrategy的对象,参数如下:

    public Factory(long nowMillis, Request request, Response cacheResponse) {
      this.nowMillis = nowMillis;
      this.request = request;
      this.cacheResponse = cacheResponse;

      if (cacheResponse != null) {
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        Headers headers = cacheResponse.headers();
        //获取cacheReposne中的header中值
        for (int i = 0, size = headers.size(); i < size; i++) {
          String fieldName = headers.name(i);
          String value = headers.value(i);
          if ("Date".equalsIgnoreCase(fieldName)) {
            servedDate = HttpDate.parse(value);
            servedDateString = value;
          } else if ("Expires".equalsIgnoreCase(fieldName)) {
            expires = HttpDate.parse(value);
          } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
            lastModified = HttpDate.parse(value);
            lastModifiedString = value;
          } else if ("ETag".equalsIgnoreCase(fieldName)) {
            etag = value;
          } else if ("Age".equalsIgnoreCase(fieldName)) {
            ageSeconds = HttpHeaders.parseSeconds(value, -1);
          }
        }
      }
    }

    /**
     * Returns a strategy to satisfy {@code request} using the a cached response {@code response}.
     */
    public CacheStrategy get() {
      CacheStrategy candidate = getCandidate();

      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        return new CacheStrategy(null, null);
      }

      return candidate;
    }
    /**
     * Returns a strategy to satisfy {@code request} using the a cached response {@code response}.
     */
    public CacheStrategy get() {
      //获取当前的缓存策略
      CacheStrategy candidate = getCandidate();
     //如果是网络请求不为null并且请求里面的cacheControl是只用缓存
      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        //使用只用缓存的策略
        return new CacheStrategy(null, null);
      }
      return candidate;
    }

    /** Returns a strategy to use assuming the request can use the network. */
    private CacheStrategy getCandidate() {
      // No cached response.
      //如果没有缓存响应,返回一个没有响应的策略
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }
       //如果是https,丢失了握手,返回一个没有响应的策略
      // Drop the cached response if it's missing a required handshake.
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }
     
      // 响应不能被缓存
      // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }
     
      //获取请求头里面的CacheControl
      CacheControl requestCaching = request.cacheControl();
      //如果请求里面设置了不缓存,则不缓存
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }
      //获取响应的年龄
      long ageMillis = cacheResponseAge();
      //获取上次响应刷新的时间
      long freshMillis = computeFreshnessLifetime();
      //如果请求里面有最大持久时间要求,则两者选择最短时间的要求
      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      long minFreshMillis = 0;
      //如果请求里面有最小刷新时间的限制
      if (requestCaching.minFreshSeconds() != -1) {
         //用请求中的最小更新时间来更新最小时间限制
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }
      //最大验证时间
      long maxStaleMillis = 0;
      //响应缓存控制器
      CacheControl responseCaching = cacheResponse.cacheControl();
      //如果响应(服务器)那边不是必须验证并且存在最大验证秒数
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        //更新最大验证时间
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }
     //响应支持缓存
       //持续时间+最短刷新时间<上次刷新时间+最大验证时间 则可以缓存
      //现在时间(now)-已经过去的时间(sent)+可以存活的时间<最大存活时间(max-age)
      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
       //缓存响应
        return new CacheStrategy(null, builder.build());
      }
    
      //如果想缓存request,必须要满足一定的条件
      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        //没有条件则返回一个定期的request
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }
      
      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      //返回有条件的缓存request策略
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

通过上面分析,我们可以发现,OKHTTP实现的缓存策略实质上就是大量的if/else判断,这些其实都是和RFC标准文档里面写死的。
上面说了这么多,那么咱们要开始今天的主题了----CacheInterceptor类

六、 CacheInterceptor 类详解

BridgeInterceptor :负责将请求返回 关联的保存到缓存中。客户端和服务器根据一定的机制(策略CacheStrategy ),在需要的时候使用缓存的数据作为网络响应,节省了时间和宽带。

老规矩上源码:

  //CacheInterceptor.java
 @Override 
 public Response intercept(Chain chain) throws IOException {
    //如果存在缓存,则从缓存中取出,有可能为null
    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;
     //缓存非空判断,
    if (cache != null) {
      cache.trackResponse(strategy);
    }
    //缓存策略不为null并且缓存响应是null
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
     //禁止使用网络(根据缓存策略),缓存又无效,直接返回
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }
     //缓存有效,不使用网络
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    //缓存无效,执行下一个拦截器
    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }
     //本地有缓存,根据条件选择使用哪个响应
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        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)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

简单的说下上述流程:
1、如果配置缓存,则从缓存中取一次,不保证存在
2、缓存策略
3、缓存监测
4、禁止使用网络(根据缓存策略),缓存又无效,直接返回
5、缓存有效,不使用网络
6、缓存无效,执行下一个拦截器
7、本地有缓存,根具条件选择使用哪个响应
8、使用网络响应
9、 缓存到本地
大体流程分析完,那么咱们再详细分析下。
首先说到了缓存就不得不提下OKHttp里面的Cache.java类和InternalCache.java那么咱们就简单的聊下这两个类

(一)、Cache.java类

Cache

1、基本特征

private final DiskLruCache cache; 

public Cache(File directory, long maxSize) {  
    this(directory, maxSize, FileSystem.SYSTEM);  
  }  
Cache(File directory, long maxSize, FileSystem fileSystem) {  
    this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);  
}  

通过上面代码可知

2、既然是Cache,那么一定会有"增"、"删"、"改"、"查"。

(1) ”增“操作——put()方法
CacheRequest put(Response response) {
    String requestMethod = response.request().method();
    //判断请求如果是"POST"、"PATCH"、"PUT"、"DELETE"、"MOVE"中的任何一个则调用DiskLruCache.remove(urlToKey(request));将这个请求从缓存中移除出去。
    if (HttpMethod.invalidatesCache(response.request().method())) {
      try {
        remove(response.request());
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
      return null;
    }
    //判断请求如果不是Get则不进行缓存,直接返回null。官方给的解释是缓存get方法得到的Response效率高,其它方法的Response没有缓存效率低。通常通过get方法获取到的数据都是固定不变的的,因此缓存效率自然就高了。其它方法会根据请求报文参数的不同得到不同的Response,因此缓存效率自然而然就低了。
    if (!requestMethod.equals("GET")) {
      // 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.
      return null;
    }
     //判断请求中的http数据包中headers是否有符号"*"的通配符,有则不缓存直接返回null
    if (HttpHeaders.hasVaryAll(response)) {
      return null;
    }
    //由Response对象构建一个Entry对象,Entry是Cache的一个内部类
    Entry entry = new Entry(response);
    //通过调用DiskLruCache.edit();方法得到一个DiskLruCache.Editor对象。
    DiskLruCache.Editor editor = null;
    try {
      editor = cache.edit(key(response.request().url()));
      if (editor == null) {
        return null;
      }
      //把这个entry写入
      //方法内部是通过Okio.buffer(editor.newSink(ENTRY_METADATA));获取到一个BufferedSink对象,随后将Entry中存储的Http报头数据写入到sink流中。
      entry.writeTo(editor);
      //构建一个CacheRequestImpl对象,构造器中通过editor.newSink(ENTRY_BODY)方法获得Sink对象
      return new CacheRequestImpl(editor);
    } catch (IOException e) {
      abortQuietly(editor);
      return null;
    }
  }

总结一下上述步骤
第一步,先判断是不是一个正常的请求(get,post等)
第二步,由于只支持get请求,非get请求直接返回
第三步,通配符过滤
第四步,通过上述检验后开始真正的缓存流程,new一个Entry
第五步,获取一个DiskLruCache.Editor对象
第六步,通过DiskLruCache.Edito写入数据
第七步,返回数据
PS:关于key()方法在remove里面详解

上面使用到了remove方法,莫非就是"删"的操作,那咱们来看下

(2) ”删“操作——remove()方法
  void remove(Request request) throws IOException {
    cache.remove(key(request.url()));
  }
  public static String key(HttpUrl url) {
    return ByteString.encodeUtf8(url.toString()).md5().hex();
  }

果然remove就是传说中的"删除"操作,
key()这个方法原来就说获取url的MD5和hex生成的key

(3) ”改“操作——update()方法
void update(Response cached, Response network) {
    //用response构造一个Entry对象
    Entry entry = new Entry(network);
    //从命中缓存中获取到的DiskLruCache.Snapshot
    DiskLruCache.Snapshot snapshot = ((CacheResponseBody) cached.body()).snapshot;
    //从DiskLruCache.Snapshot获取DiskLruCache.Editor()对象
    DiskLruCache.Editor editor = null;
    try {
      editor = snapshot.edit(); // Returns null if snapshot is not current.
      if (editor != null) {
        //将entry写入editor中
        entry.writeTo(editor);
        editor.commit();
      }
    } catch (IOException e) {
      abortQuietly(editor);
    }
  }

根据上述代码大体流程如下:
第一步,首先要获取entry对象
第二步,获取DiskLruCache.Editor对象
第三步,写入entry对象

(4) ”查“操作——get()方法
 Response get(Request request) {
    //获取url经过MD5和HEX的key
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
     //根据key来获取一个snapshot,由此可知我们的key-value里面的value对应的是snapshot
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }
    //利用前面的Snapshot创建一个Entry对象。存储的内容是响应的Http数据包Header部分的数据。snapshot.getSource得到的是一个Source对象 (source是okio里面的一个接口)
    try {
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }

    //利用entry和snapshot得到Response对象,该方法内部会利用前面的Entry和Snapshot得到响应的Http数据包Body(body的获取方式通过snapshot.getSource(ENTRY_BODY)得到)创建一个CacheResponseBody对象;再利用该CacheResponseBody对象和第三步得到的Entry对象构建一个Response的对象,这样该对象就包含了一个网络响应的全部数据了。
    Response response = entry.response(snapshot);
    //对request和Response进行比配检查,成功则返回该Response。匹配方法就是url.equals(request.url().toString()) && requestMethod.equals(request.method()) && OkHeaders.varyMatches(response, varyHeaders, request);其中Entry.url和Entry.requestMethod两个值在构建的时候就被初始化好了,初始化值从命中的缓存中获取。因此该匹配方法就是将缓存的请求url和请求方法跟新的客户请求进行对比。最后OkHeaders.varyMatches(response, varyHeaders, request)是检查命中的缓存Http报头跟新的客户请求的Http报头中的键值对是否一样。如果全部结果为真,则返回命中的Response。
    if (!entry.matches(request, response)) {
      Util.closeQuietly(response.body());
      return null;
    }

    return response;
  }

总结上面流程大体是:
第一步 获取key
第一步 获取DiskLruCache.Snapshot对象
第三步 获取Entry对象
第四步 获取response
第五步 检查是response

通过对上述增删改查的分析,我们可以得出如下结论

方法 返回值
DiskLruCache.get(String) 可以获取DiskLruCache.Snapshot
DiskLruCache.remove(String) 可以移除请求
DiskLruCache.edit(String) 可以获得一个DiskLruCache.Editor对象
DiskLruCache.Editor.newSink(int) 可以获得一个sink流
DiskLruCache.Snapshot.getSource(int) 可以获取一个Source对象。
DiskLruCache.Snapshot.edit() 可以获得一个DiskLruCache.Editor对象

DiskLruCache是OKHTTP的缓存的精髓,由于篇幅限制,在下一章讲解

上一篇下一篇

猜你喜欢

热点阅读