Cache#update()

4 阅读1分钟

Cache#update()

OkHttp收到GET request以后,会先尝试在本地查找满足reuse条件的stored response,但是只找到满足其中部分条件的:

  • uri match
  • method match
  • varys match

于是根据这个request中携带的validator,手动帮这个request构造一个条件请求发往下一跳。当收到304以后,Okhttp会调用 Cache#update()来更新本地缓存。

Okhttp实现 vs rfc 7234

rfc 7234在 4.3.4. Freshening Stored Responses upon Validation中 定义了当收到304时如何更新缓存。Okhttp的实现略有不同。

更新范围

rfc 7234中会根据304 response中的validator来select一个或者多个待更新的stored reponse。OkHttp则总是使用在发起validation以前就提前选好的 一个cache response。

Warning header fields处理

 If a stored response is selected for update, the cache MUST:

   o  delete any Warning header fields in the stored response with
      warn-code 1xx (see Section 5.5);

   o  retain any Warning header fields in the stored response with
      warn-code 2xx; and,

   o  use other header fields provided in the 304 (Not Modified)
      response to replace all instances of the corresponding header
      fields in the stored response.

这是rfc 7234定义的header方式;OkHttp则使用直接覆盖来更新:

// Cache#update()
internal fun update(cached: Response, network: Response) {
    val entry = Entry(network)
    val snapshot = (cached.body as CacheResponseBody).snapshot
    var editor: DiskLruCache.Editor? = null
    try {
        editor = snapshot.edit() ?: return // edit() returns null if snapshot is not current.
        // 304 response直接覆盖掉cache response的header
        entry.writeTo(editor)
        editor.commit()
    } catch (_: IOException) {
        abortQuietly(editor)
    }
}

//Entry#writeTo()
@Throws(IOException::class)
fun writeTo(editor: DiskLruCache.Editor) {
    // 指定覆盖ENTRY_METADATA部分
    editor.newSink(ENTRY_METADATA).buffer().use { sink ->
        // 这里时覆盖内容
        sink.writeUtf8(url.toString()).writeByte('\n'.toInt())
        sink.writeUtf8(requestMethod).writeByte('\n'.toInt())
        sink.writeDecimalLong(varyHeaders.size.toLong()).writeByte('\n'.toInt())
        for (i in 0 until varyHeaders.size) {
            sink.writeUtf8(varyHeaders.name(i))
                .writeUtf8(": ")
                .writeUtf8(varyHeaders.value(i))
                .writeByte('\n'.toInt())
//..