OkHttp3源码解读三:缓存层

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/www1575066083/article/details/81484521

1、基本介绍:

  • Cache缓存层:管理本地缓存,当用户的网络请求在本地已有符合要求的缓存时,OkHttp会直接从缓存中返回结果,从而节省网络开销。
  • OkHttp的缓存工作都是在CacheInterceptor中完成的。

2、重要类:

2.1、Cache:

  • Cache管理器,其内部包含一个DiskLruCache将cache写入文件系统。
  • Cache内部通过requestCount,networkCount,hitCount三个统计指标来优化缓存效率。

2.2、CacheStrategy:

  • 缓存策略。其内部维护一个request和response,通过指定request和response来描述是通过网络还是缓存获取response,抑或二者同时使用。
  • CacheStrategy$Factory:缓存策略工厂类根据实际请求返回对应的缓存策略。
    [CacheStrategy$Factory]

        /**
         * 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 use assuming the request can use the network. */
        private CacheStrategy getCandidate() {
          // 若本地没有缓存,发起网络请求
          if (cacheResponse == null) {
            return new CacheStrategy(request, null);
          }

          // 如果当前请求是HTTPS,而缓存没有TLS握手,重新发起网络请求
          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);
          }


          //如果当前的缓存策略是不缓存或者是conditional get,发起网络请求
          CacheControl requestCaching = request.cacheControl();
          if (requestCaching.noCache() || hasConditions(request)) {
            return new CacheStrategy(request, null);
          }

          //ageMillis:缓存age
          long ageMillis = cacheResponseAge();
          //freshMillis:缓存保鲜时间
          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());
          }

          //如果 age + min-fresh >= max-age && age + min-fresh < max-age + max-stale,则虽然缓存过期了,     //但是缓存继续可以使用,只是在头部添加 110 警告码
          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());
          }

          // 发起conditional get请求
          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 {
            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();
          return new CacheStrategy(conditionalRequest, cacheResponse);
        }

2.3、CahceInterceptor:

  • 实际的缓存工作都是在CacheInterceptor中完成的。
[CacheInterceptor.java]
    @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);
        }

        //如果当前缓存不符合要求,将其close
        if (cacheCandidate != null && cacheResponse == null) {
          closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
        }

        // 如果不能使用网络,同时又没有符合条件的缓存,直接抛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 (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());
          }
        }

        // 如果既有缓存,同时又发起了请求,说明此时是一个Conditional Get请求
        if (cacheResponse != null) {
          // 如果服务端返回的是NOT_MODIFIED,缓存有效,将本地缓存和网络响应做合并
          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)) {
            // 将网络响应写入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;
      }

猜你喜欢

转载自blog.csdn.net/www1575066083/article/details/81484521