美文网首页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