【Android】OkHttp系列(四):缓存拦截器CacheInterceptor

该系列OkHttp源码分析基于OkHttp3.14.0版本

概述

缓存响应以及返回之前的缓存的响应。整个拦截器可以分为两个部分,第一个部分为读取缓存,第二个部分为保存缓存。

需要注意的是,如果没有配置缓存管理的话,OkHttp是不会帮你缓存的,每次请求都会向服务器发起。但是OkHttp是自带了一个缓存管理的,一个名为Cache的类。不过在OkHttpClient的缓存管理那里并没有默认配置。该类只需要两个参数,一个为缓存的文件目录(CacheDir),一个为最大的缓存大小(maxSize)。

源码分析

返回缓存

@Override public Response intercept(Chain chain) throws IOException {
  //获取用户自己配置的缓存设置
  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);
  }

  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.
  // 没有缓存的响应,又不发送网络请求,返回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();
  }

  // If we don't need the network, we're done.
  // 不需要网络请求,直接返回缓存的响应
  if (networkRequest == null) {
    return cacheResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build();
  }

  ...省略部分代码
  // If we have a cache response too, then we're doing a conditional get.
  // 之前有缓存的响应结果
  if (cacheResponse != null) {
    if (networkResponse.code() == HTTP_NOT_MODIFIED) {// 304
      //服务器告诉客户端,之前的请求结果可以继续使用,所以这里使用缓存结果
      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()).
      // 在合并标头之后但在剥离Content-Encoding标头之前(由initContentStream()执行),更新缓存。
      cache.trackConditionalCacheHit();
      cache.update(cacheResponse, response);//更新缓存
      return response;
    } else {
      closeQuietly(cacheResponse.body());
    }
  }

    ...省略部分代码
}

根据源码可以看到,如果cache为null的情况下,cacheResponse也会为null,也就导致永远不会出现缓存了。这也证明我前面说的,OkHttp虽然自带了一个缓存管理类,但是却需要我们自己去配置到OkHttpClient

看完源码之后我们可以发现,是否返回缓存主要由3个参数决定的,一个是networkRequest,一个是cacheResponse,一个是请求后的状态码。

状态码的比较好理解,304表示服务器告诉客户端,之前缓存的资源是可以使用的。

另外两个变量的话,设计到OkHttp的缓存策略了,这个后面再讲。主要涉及到这几个类CacheCacheStrategy

缓存响应

返回缓存看完了,现在我们来看看OkHttp在缓存拦截器(CacheInterceptor)中是如何去缓存服务端的响应的。

//下面开始请求网络
Response networkResponse = null;
try {
  networkResponse = chain.proceed(networkRequest);
} finally {
  // If we're crashing on I/O or otherwise, don't leak the cache body.
  // 如果我们在I / O或其他方面崩溃,请不要泄漏高速缓存主体。
  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) {// 304
      ...省略部分代码
    // Update the cache after combining headers but before stripping the
    // Content-Encoding header (as performed by initContentStream()).
    // 在合并标头之后但在剥离Content-Encoding标头之前(由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.
    }
  }
}

可以看到,在拦截器中并没有设计到如何缓存的细节,只是判断了当前的响应是否符合缓存条件,具体的细节放在了cache这个变量中了。目前的话暂时不分析具体的细节了,等以后再考虑详细分析OkHttp的缓存细节。

发布了24 篇原创文章 · 获赞 7 · 访问量 8683

猜你喜欢

转载自blog.csdn.net/d745282469/article/details/104296583
今日推荐