源码分析三:OkHttp—CacheInterceptor

本文分析okHttp缓存拦截器,可以首先了解http缓存原理: 彻底弄懂HTTP缓存机制及原理

缓存拦截器基本流程

  1. 读取候选缓存;
  2. 创建缓存策略(根据头信息,判断强制缓存,对比缓存等策略);
  3. 根据策略,不使用网络,缓存又没有直接报错;
  4. 根据策略,不使用网络,有缓存就直接返回;
  5. 前面个都没有返回,读取网络结果(跑下一个拦截器);
  6. 接收到的网络结果,如果是code 304, 使用缓存,返回缓存结果(对比缓存)
  7. 读取网络结果;
  8. 对数据进行缓存;
  9. 删除无效缓存;
  10. 返回网络读取的结果。

源码解读

  @Override public Response intercept(Chain chain) throws IOException {
   //1. 读取候选缓存;
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();

    //2. 创建缓存策略(强制缓存,对比缓存等策略);
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }

    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();
    }

    // 4. 根据策略,不使用网络,有缓存就直接返回;
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
     // 5. 前面个都没有返回,读取网络结果(跑下一个拦截器);
      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());
      }
    }

   //6. 接收到的网络结果,如果是code 304, 使用缓存,返回缓存结果(对比缓存)
    // 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());
      }
    }

   //7. 读取网络结果;
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    //8. 对数据进行缓存;
    if (HttpHeaders.hasBody(response)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }
    //9. 返回网络读取的结果。
    return response;
  }复制代码

拆开来看:

1、读取候选缓存

    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
复制代码

2、创建缓存策略(根据头信息,判断强制缓存,对比缓存等策略)

 CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;复制代码

构建请求策略,进入这个方法。
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();
    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);
      }
    }
  }
}复制代码
“Date”,“Expires”,“Last-Modified”,“ETag”,“Age”这几个单词有没有很熟?之前推荐的文章里有讲过。全都都是header里的标志,这里就是用来获取header里标志的内容。
/** The request to send on the network, or null if this call doesn't use the network. */
public final @Nullable Request networkRequest;

/** The cached response to return or validate; or null if this call doesn't use a cache. */
public final @Nullable Response cacheResponse;复制代码

根据CacheStrategy里的注释,可以知道

  • networkRequest:无网络即为null,有网络则为将要发送的请求在的网络部分。
  • cacheResponse:无缓存即为null,有缓存则为缓存的响应,用来返回或验证。


3、根据策略,无网络、没有缓存,直接报错

无网络无cache直接报错,“Unsatisfiable Request (only-if-cached)”错误代码504。

 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();
    }复制代码

4、根据策略,无网络、有缓存,就直接返回

如果你写自定义interceptor的时候遇到这个error,不一定是你代码的问题,很可能是服务端没有配合。

 // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }复制代码

5、前面个都没有返回,读取网络结果(跑下一个拦截器)

Response networkResponse = null;
    try {
     // 5. 前面个都没有返回,读取网络结果(跑下一个拦截器);
      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());
      }
    }复制代码

6、接收到的网络结果,如果是code 304, 使用缓存,返回缓存结果(对比缓存)

// 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());
      }
    }
复制代码

7、读取网络结果;

 Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();复制代码

8、对数据进行缓存;

if (HttpHeaders.hasBody(response)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }复制代码

其中cacheWritingResponse()通过Okio以及DiskLruCache来实现。

9、删除无效缓存

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

10、返回网络读取的结果。

 return response;复制代码



转载于:https://juejin.im/post/5aa5ef0e6fb9a028c22a682a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值