跳到内容

缓存

OkHttp 实现了一个可选的、默认关闭的缓存。OkHttp 旨在实现符合 RFC 且实用的缓存行为,在存在歧义时遵循 Firefox/Chrome 等常见真实世界浏览器和服务器的行为。

基本用法

  private val client: OkHttpClient = OkHttpClient.Builder()
      .cache(Cache(
          directory = File(application.cacheDir, "http_cache"),
          // $0.05 worth of phone storage in 2020
          maxSize = 50L * 1024L * 1024L // 50 MiB
      ))
      .build()

EventListener 事件

缓存事件通过 EventListener API 公开。典型场景如下所示。

缓存命中

在理想情况下,缓存可以直接满足请求,无需进行任何条件性的网络调用。这将跳过 DNS、连接网络和下载响应体等正常事件。

根据 HTTP RFC 的建议,文档的最大有效期默认为其在服务时基于“Last-Modified”的年龄的 10%。包含查询的 URI 不使用默认过期日期。

  • CallStart
  • CacheHit
  • CallEnd

缓存未命中

在缓存未命中时,会看到正常的请求事件,但会额外出现一个事件表明缓存的存在。如果项目尚未从网络读取、无法缓存,或者已超出基于响应缓存头指定的生命周期,则通常会出现缓存未命中。

  • CallStart
  • CacheMiss
  • ProxySelectStart
  • … 标准事件 …
  • CallEnd

条件缓存命中

当缓存标志要求检查缓存结果是否仍然有效时,会首先接收到一个 cacheConditionalHit 事件,然后是缓存命中或未命中。至关重要的是,在缓存命中场景下,服务器不会发送响应体。

响应将具有非空的 cacheResponsenetworkResponse。仅当响应代码是 HTTP/1.1 304 Not Modified 时,才会将 cacheResponse 用作顶层响应。

  • CallStart
  • CacheConditionalHit
  • ConnectionAcquired
  • … 标准事件…
  • ResponseBodyEnd (0 字节)
  • CacheHit
  • ConnectionReleased
  • CallEnd

缓存目录

缓存目录必须由单个实例独占。

可以在不再需要时删除缓存。然而,这可能会破坏缓存的目的,因为缓存旨在在应用程序重启之间持久化。

cache.delete()

修剪缓存

可以使用 evictAll 临时清理整个缓存以释放空间。

cache.evictAll()

可以使用 urls 迭代器移除单个项目。这通常在用户通过下拉刷新等操作强制刷新后进行。

    val urlIterator = cache.urls()
    while (urlIterator.hasNext()) {
      if (urlIterator.next().startsWith("https://www.google.com/")) {
        urlIterator.remove()
      }
    }

故障排除

  1. 有效的可缓存响应未被缓存

请确保您完整地读取响应,因为除非响应被完整读取、取消或停止,否则不会被缓存。

覆盖正常的缓存行为

请参阅缓存文档。 https://square.ac.cn/okhttp/4.x/okhttp/okhttp3/-cache/