Okhttp version: com. Squareup. Okhttp3: okhttp: 3.12.1

Background of this article

I didn’t know much about the okHTTP cache mechanism before, and when I analyzed the OKHTTP network request process, I also touched on this. I thought that the cache part had nothing to do with the request process, so I decided to read the source code carefully.

Most of the online articles on okHTTP caching are divided into two parts:

  • How does OKHTTP implement the HTTP caching mechanism
  • DiskLruCache source code parsing

This article does not cover the HTTP caching mechanism, but focuses on the source parsing of the local cache (i.e. how data streams are written locally). In the process of reading, I have a question: The data returned from the network is stored in the response Header, which is clearly visible locally, through cache.put (), but this is just the response Header. What about the body? Although know is in CacheInterceptor cacheWritingResponse () writes, post code, convenient:

–> CacheInterceptor.java

private Response cacheWritingResponse(final CacheRequest cacheRequest, Response response)
      throws IOException {
    // Some apps return a null body; for compatibility we treat that like a null cache request.
    if (cacheRequest == null) return response;
    Sink cacheBodyUnbuffered = cacheRequest.body();
    if (cacheBodyUnbuffered == null) return response;

    final BufferedSource source = response.body().source();
    final BufferedSink cacheBody = Okio.buffer(cacheBodyUnbuffered);

    Source cacheWritingSource = new Source() {
      boolean cacheRequestClosed;

      @Override public long read(Buffer sink, long byteCount) throws IOException {
        long bytesRead;
        try {
          bytesRead = source.read(sink, byteCount);
        } catch (IOException e) {
          if(! cacheRequestClosed) { cacheRequestClosed =true;
            cacheRequest.abort(); // Failed to write a complete cache response.
          }
          throw e;
        }

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

        // The read method doesn't see a call anywhere, even though it knows it must be written locally at the end
        sink.copyTo(cacheBody.buffer(), sink.size() - bytesRead, bytesRead);
        cacheBody.emitCompleteSegments();
        return bytesRead;
      }

      @Override public Timeout timeout(a) {
        return source.timeout();
      }

      @Override public void close(a) throws IOException {
        if(! cacheRequestClosed && ! discard(this, HttpCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
          cacheRequestClosed = true; cacheRequest.abort(); } source.close(); }}; String contentType = response.header("Content-Type");
    long contentLength = response.body().contentLength();
    // The cacheWritingSource read method needs to be called to write the body locally, but here
    // Simply wrap cacheWritingSource and assign it to the RealResponseBody member variable
    return response.newBuilder()
        .body(new RealResponseBody(contentType, contentLength, Okio.buffer(cacheWritingSource)))
        .build();
  }

Copy the code

As above, Chinese annotation expressed doubt, don’t know where is the read method is called cacheWritingSource, continues the upstream of the interceptor BridgeInterceptor, RetryAndFollowUpInterceptor search, take a look at the data returned, Debug the stack where the read method is called.

The debug failures

CacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource: cacheWritingSource

However, the break point is not reached!!

How can data streams be stored locally without using the read method? Is it not cached locally at all? CD to cache directory to have a look:

In addition to the journal file, only two.tmp files are seen.

There is an internal class Entry in DiskLruCache. Each Entry object corresponds to a URL and is stored in the LinkedHashMap. There are four files in the Entry, respectively:

  • Key.0 (the file where the Header is finally saved)
  • Key.0.tmp (temporarily save Header file)
  • Key.1 (the file that will ultimately save the Body)
  • Key.1.tmp (temporary save Body file)

TMP and key.1.tmp are all operated first when data flows are written to the local computer. After data flows are written to the local computer, key.0.tmp and key.1.tmp are renamed to key.0 when the data flows are closed. Key. 1.

TMP files will be renamed key.0 and key.1 when the data is written. The data stream is not written at all. Take a look at the contents of these two files:

–> Save the Header key.0.tmp file

–> Save the Body key.1.tmp file

The body data is not written to the key.1.tmp file. The body data is not written to the key.1.tmp file. Because data is already written to cache.put ().

By now, you know that the data stream really wasn’t written locally.

The truth

* * * * * * * * * * * * * * * * * * * * * * * * * * * *

Key.0, key.1: key.

This is the source, response.body().bytes() is called, which reads the data, and then the read method of the inner class in the original image is called, and then the stream is written locally.

Originally, only when the data is actively read, can the data stream be triggered to write, which explains the question of this article, there is really no place to call the method of reading the stream, we need to call the read

If you don’t read network data when it comes back, there’s no need to cache it locally because you don’t use it.

Finally, post a Source inclusion diagram (if the current URL is not cached locally) :