okhttp学习系列之CacheInterceptor拦截器

Cache-Control字段

在正式介绍CacheInterceptor拦截器之前,我们有必要复习下Cache-Control字段。因为CacheInterceptor实际上是对Cache-Control字段的逻辑实现

常见的取值及含义是

no-store 彻底禁用缓存,所有内容都不会被缓存到缓存或临时文件中
no-cache 在浏览器使用缓存前,会往返对比ETag,如果ETag没变,返回304,则使用缓存

public

所有的内容都将被缓存

private 缓存仅客户端可以缓存,代理服务器不可缓存
max-age 缓存的内容将在xxx秒后失效,这个选项只有在http1.1可用,并如果和Last-Modified一起使用时,优先级较高

代码逻辑

完整的代码请参看https://gitee.com/you-zijun/okhttp/blob/c2fa722dfc3e2d5e823af404c3ed30f112418eac/okhttp/src/main/kotlin/okhttp3/internal/cache/CacheInterceptor.kt

代码分三部分

1. 进行cache和request的预处理

//首先应该留意到这个构造方法, 需要初始化一个Cache对象, 而Cache类是final类型的, 所有这个地方不需要自己实现Cache的逻辑
class CacheInterceptor(internal val cache: Cache?) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val call = chain.call()
    //从cache中获取当前的CacheResponse. 这个cacheCandidate实际上就是一个Response
    val cacheCandidate = cache?.get(chain.request())

    val now = System.currentTimeMillis()

    //上面从cache, 即使上是从DiskLruCache中读取了CacheResponse,但是这个CacheResponse不一定可用, 可能会受到Cache Control中的
    //过期等影响, 同样,原始的Request也不一定可用个, 需要利用CacheStrategy来综合判断处理过的request和cache response,也就是后面
    //获取到的networkRequest和cacheResponse
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

2. 进行网络请求,拿到network response


    // If we're forbidden from using the network and the cache is insufficient, fail.
    // 如果即没有request, 也没有response, 那就直接返回一个空结果好了
    if (networkRequest == null && cacheResponse == null) {
      return Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(HTTP_GATEWAY_TIMEOUT)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build().also {
            listener.satisfactionFailure(call, it)
          }
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {
      //如果没有network request, 则返回cacheResponse
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }

    var networkResponse: Response? = 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) {
        cacheCandidate.body?.closeQuietly()
      }
    }

3. 对返回结果进行保存更新等处理

 // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        // 如果服务端返回没有修改, 则返回cacheResponse 略加修改,返回
        val 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
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

    // 更新cache
    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()

    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        val cacheRequest = cache.put(response)
        return cacheWritingResponse(cacheRequest, response).also {
          if (cacheResponse != null) {
            // This will log a conditional cache miss only.
            listener.cacheMiss(call)
          }
        }
      }
      //post put delete move方法是灭有cache的
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }

上面的核心在于第一步,对cache response和network request进行预处理部分,该部分如下

    /** Returns a strategy to use assuming the request can use the network. */
    private fun computeCandidate(): CacheStrategy {
      // No cached response.
      if (cacheResponse == null) {
        return CacheStrategy(request, null)
      }

      // Drop the cached response if it's missing a required handshake.
      // 如果是https请求, 但是cache response没有进行握手操作.
      if (request.isHttps && cacheResponse.handshake == null) {
        return 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)) {
        //判断上一次的response是否可以被使用
        return CacheStrategy(request, null)
      }

      val requestCaching = request.cacheControl
      if (requestCaching.noCache || hasConditions(request)) {
        // 如果request用的是no cache, 则不使用cache
        return CacheStrategy(request, null)
      }

      //下面的逻辑是根据过期时间进行判断
      val responseCaching = cacheResponse.cacheControl

      val ageMillis = cacheResponseAge()
      var freshMillis = computeFreshnessLifetime()

      if (requestCaching.maxAgeSeconds != -1) {
        freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))
      }

      var minFreshMillis: Long = 0
      if (requestCaching.minFreshSeconds != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())
      }

      var maxStaleMillis: Long = 0
      if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())
      }

      if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        val builder = cacheResponse.newBuilder()
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"")
        }
        val oneDayMillis = 24 * 60 * 60 * 1000L
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"")
        }
        return CacheStrategy(null, builder.build())
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      // 这部分的逻辑应该是找到了,进行etag时间进行匹配
      val conditionName: String
      val conditionValue: String?
      when {
        etag != null -> {
          conditionName = "If-None-Match"
          conditionValue = etag
        }

        lastModified != null -> {
          conditionName = "If-Modified-Since"
          conditionValue = lastModifiedString
        }

        servedDate != null -> {
          conditionName = "If-Modified-Since"
          conditionValue = servedDateString
        }

        else -> return CacheStrategy(request, null) // No condition! Make a regular request.
      }

      val conditionalRequestHeaders = request.headers.newBuilder()
      conditionalRequestHeaders.addLenient(conditionName, conditionValue!!)

      val conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build()
      return CacheStrategy(conditionalRequest, cacheResponse)
    }

猜你喜欢

转载自blog.csdn.net/weixin_43662090/article/details/114535145
今日推荐