Android

CacheInterceptor

2019-10-24  本文已影响0人  大佬的上半生
CacheInterceptor缓存过滤器、
@Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val cacheCandidate = cache?.get(chain.request())

    val now = System.currentTimeMillis()
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

    cache?.trackResponse(strategy)
    
    if (cacheCandidate != null && cacheResponse == null) {
      cacheCandidate.body?.closeQuietly()
    }

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

    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build()
    }

    var networkResponse: Response? = null
    try {
      networkResponse = chain.proceed(networkRequest)
    } finally {
      if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        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.update(cacheResponse, response)
        return response
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

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

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

    return response
  }

  /**
   * Returns a new source that writes bytes to [cacheRequest] as they are read by the source
   * consumer. This is careful to discard bytes left over when the stream is closed; otherwise we
   * may never exhaust the source stream and therefore not complete the cached response.
   */
  @Throws(IOException::class)
  private fun cacheWritingResponse(cacheRequest: CacheRequest?, response: Response): Response {
    // Some apps return a null body; for compatibility we treat that like a null cache request.
    if (cacheRequest == null) return response
    val cacheBodyUnbuffered = cacheRequest.body()

    val source = response.body!!.source()
    val cacheBody = cacheBodyUnbuffered.buffer()

    val cacheWritingSource = object : Source {
      var cacheRequestClosed: Boolean = false

      @Throws(IOException::class)
      override fun read(sink: Buffer, byteCount: Long): Long {
        val bytesRead: Long
        try {
          bytesRead = source.read(sink, byteCount)
        } catch (e: IOException) {
          if (!cacheRequestClosed) {
            cacheRequestClosed = true
            cacheRequest.abort() // Failed to write a complete cache response.
          }
          throw e
        }

        if (bytesRead == -1L) {
          if (!cacheRequestClosed) {
            cacheRequestClosed = true
            cacheBody.close() // The cache response is complete!
          }
          return -1
        }

        sink.copyTo(cacheBody.buffer, sink.size - bytesRead, bytesRead)
        cacheBody.emitCompleteSegments()
        return bytesRead
      }

      override fun timeout(): Timeout {
        return source.timeout()
      }

      @Throws(IOException::class)
      override fun close() {
        if (!cacheRequestClosed &&
            !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
          cacheRequestClosed = true
          cacheRequest.abort()
        }
        source.close()
      }
    }

    val contentType = response.header("Content-Type")
    val contentLength = response.body.contentLength()
    return response.newBuilder()
        .body(RealResponseBody(contentType, contentLength, cacheWritingSource.buffer()))
        .build()
  }

  companion object {

    private fun stripBody(response: Response?): Response? {
      return if (response?.body != null) {
        response.newBuilder().body(null).build()
      } else {
        response
      }
    }

    /** Combines cached headers with a network headers as defined by RFC 7234, 4.3.4. */
    private fun combine(cachedHeaders: Headers, networkHeaders: Headers): Headers {
      val result = Headers.Builder()

      for (index in cachedHeaders.names().indices) {
        val fieldName = cachedHeaders.name(index)
        val value = cachedHeaders.value(index)
        if ("Warning".equals(fieldName, ignoreCase = true) && value.startsWith("1")) {
          // Drop 100-level freshness warnings.
          continue
        }
        if (isContentSpecificHeader(fieldName) ||
            !isEndToEnd(fieldName) ||
            networkHeaders[fieldName] == null) {
          result.addLenient(fieldName, value)
        }
      }

      for (index in networkHeaders.names().indices) {
        val fieldName = networkHeaders.name(index)
        if (!isContentSpecificHeader(fieldName) && isEndToEnd(fieldName)) {
          result.addLenient(fieldName, networkHeaders.value(index))
        }
      }

      return result.build()
    }

    /**
     * Returns true if [fieldName] is an end-to-end HTTP header, as defined by RFC 2616,
     * 13.5.1.
     */
    private fun isEndToEnd(fieldName: String): Boolean {
      return !"Connection".equals(fieldName, ignoreCase = true) &&
          !"Keep-Alive".equals(fieldName, ignoreCase = true) &&
          !"Proxy-Authenticate".equals(fieldName, ignoreCase = true) &&
          !"Proxy-Authorization".equals(fieldName, ignoreCase = true) &&
          !"TE".equals(fieldName, ignoreCase = true) &&
          !"Trailers".equals(fieldName, ignoreCase = true) &&
          !"Transfer-Encoding".equals(fieldName, ignoreCase = true) &&
          !"Upgrade".equals(fieldName, ignoreCase = true)
    }

    /**
     * Returns true if [fieldName] is content specific and therefore should always be used
     * from cached headers.
     */
    private fun isContentSpecificHeader(fieldName: String): Boolean {
      return "Content-Length".equals(fieldName, ignoreCase = true) ||
          "Content-Encoding".equals(fieldName, ignoreCase = true) ||
          "Content-Type".equals(fieldName, ignoreCase = true)
    }
  }
执行下一个任务之前
val cacheCandidate = cache?.get(chain.request())
    val now = System.currentTimeMillis()
  //创建缓存器
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
//获取缓存
    val cacheResponse = strategy.cacheResponse
    cache?.trackResponse(strategy)
    if (cacheCandidate != null && cacheResponse == null) {
            //缓存为空关闭cacheCandidate
      cacheCandidate.body?.closeQuietly()
    }
      //没有网络&&h缓存为空构建一个新的Response 状态码为504
      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()
    }

    //如果没有网络,读取缓存
    if (networkRequest == null && cacheResponse == null) {
    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build()
    }
    var networkResponse: Response? = null
执行下一个 networkResponse = chain.proceed(networkRequest)
执行下一个任务之后
 finally {
     // 如果我们在I / O或其他方面崩溃,关闭缓存资源
  if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }

    //如果有缓存,状态为304则获取缓存
    if (cacheResponse != null) {
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        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()

     //在合并标头之后但在剥离之前更新缓存
  //  Content-Encoding标头(由initContentStream()执行)。
        cache!!.trackConditionalCacheHit()
        cache.update(cacheResponse, response)
        return response
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }
  //读取网络
    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()

    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // 将响应进项缓存
        val cacheRequest = cache.put(response)
        return cacheWritingResponse(cacheRequest, response)
      }
  //删除无效缓存
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }
  //返回结果
    return response
  }

上面基本流程就是

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

上一篇下一篇

猜你喜欢

热点阅读