美文网首页android
okhttp源码解析--缓存拦截策略

okhttp源码解析--缓存拦截策略

作者: 二妹是只猫 | 来源:发表于2019-03-14 14:08 被阅读10次
Okhttpclient流程图.png
okhttp中设置缓存非常简单,只需要在OkHttpClient中调用cache方法创建Cache对象就可以实现:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().cache(new Cache(new File("cache"),1024*1024*10)).connectTimeout(5,TimeUnit.SECONDS).build();

但具体的实现在源码中还是值得一看的。

Cache的put方法(存):
@Nullable CacheRequest put(Response response) {
    String requestMethod = response.request().method();

1    if (HttpMethod.invalidatesCache(response.request().method())) {
      try {
        remove(response.request());
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
      return null;
    }

2    if (!requestMethod.equals("GET")) {
      // Don't cache non-GET responses. We're technically allowed to cache
      // HEAD requests and some POST requests, but the complexity of doing
      // so is high and the benefit is low.
      return null;
    }

3    if (HttpHeaders.hasVaryAll(response)) {
      return null;
    }

4    Entry entry = new Entry(response);
5    DiskLruCache.Editor editor = null;
6  try {
      editor = cache.edit(key(response.request().url()));
      if (editor == null) {
        return null;
      }
      entry.writeTo(editor);
      return new CacheRequestImpl(editor);
    } catch (IOException e) {
      abortQuietly(editor);
      return null;
    }
  }
  • 1.首先判断当前调用请求是否有效,无效就拦截并且缓存中移除
  • 2.如果不是GET请求就拦截,这点很好理解POST、DELETE这些方法也没缓存的必要。
  • 3.HttpHeaders.hasVaryAll(response)判断请求头是否有效
  • 4.使用Entry将我们的请求头包装起来
  • 5.创建一个DiskLruCache对象,由此看出okhttp的缓存策略也是LRC
  • 6.先将请求url使用md5加密存起来,在调用entry.writeTo将请求头、请求方法等存起来,最后返回new CacheRequestImpl(editor)将请求体存起来。
    writTo:
 public void writeTo(DiskLruCache.Editor editor) throws IOException {
      BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));

      sink.writeUtf8(url)
          .writeByte('\n');
      sink.writeUtf8(requestMethod)
          .writeByte('\n');
      sink.writeDecimalLong(varyHeaders.size())
          .writeByte('\n');
      for (int i = 0, size = varyHeaders.size(); i < size; i++) {
        sink.writeUtf8(varyHeaders.name(i))
            .writeUtf8(": ")
            .writeUtf8(varyHeaders.value(i))
            .writeByte('\n');
      }

      sink.writeUtf8(new StatusLine(protocol, code, message).toString())
          .writeByte('\n');
      sink.writeDecimalLong(responseHeaders.size() + 2)
          .writeByte('\n');
      for (int i = 0, size = responseHeaders.size(); i < size; i++) {
        sink.writeUtf8(responseHeaders.name(i))
            .writeUtf8(": ")
            .writeUtf8(responseHeaders.value(i))
            .writeByte('\n');
      }
      sink.writeUtf8(SENT_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(sentRequestMillis)
          .writeByte('\n');
      sink.writeUtf8(RECEIVED_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(receivedResponseMillis)
          .writeByte('\n');

      if (isHttps()) {
        sink.writeByte('\n');
        sink.writeUtf8(handshake.cipherSuite().javaName())
            .writeByte('\n');
        writeCertList(sink, handshake.peerCertificates());
        writeCertList(sink, handshake.localCertificates());
        sink.writeUtf8(handshake.tlsVersion().javaName()).writeByte('\n');
      }
      sink.close();
    }

** CacheRequestImpl:**

CacheRequestImpl(final DiskLruCache.Editor editor) {
      this.editor = editor;
      this.cacheOut = editor.newSink(ENTRY_BODY);
      this.body = new ForwardingSink(cacheOut) {
        @Override public void close() throws IOException {
          synchronized (Cache.this) {
            if (done) {
              return;
            }
            done = true;
            writeSuccessCount++;
          }
          super.close();
          editor.commit();
        }
      };
    }

Cache的get方法(取):
@Nullable Response get(Request request) {
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
1     snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
2    entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }

    Response response = entry.response(snapshot);

3   if (!entry.matches(request, response)) {
      Util.closeQuietly(response.body());
      return null;
    }

    return response;
  }
  • 1.获取缓存,如何判断是否为空
  • 2.转化成包装类Entry,然后得到Response
  • 3.判断请求与响应是否匹配
    response():
public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      Request cacheRequest = new Request.Builder()
          .url(url)
          .method(requestMethod, null)
          .headers(varyHeaders)
          .build();
      return new Response.Builder()
          .request(cacheRequest)
          .protocol(protocol)
          .code(code)
          .message(message)
          .headers(responseHeaders)
          .body(new CacheResponseBody(snapshot, contentType, contentLength))
          .handshake(handshake)
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(receivedResponseMillis)
          .build();
    }
  }

缓存策略这里出要写的是它的源码,具体的应用是在CacheInterceptor中,在这篇okhttp源码解析--拦截器有介绍。

相关文章

网友评论

    本文标题:okhttp源码解析--缓存拦截策略

    本文链接:https://www.haomeiwen.com/subject/qtpqmqtx.html