OkHttp源碼解讀總結(九)--->okhttp的緩存策略

OkHttp源碼解讀總結(九)—>okhttp的緩存策略

標籤(空格分隔): OkHttp源碼 學習筆記


前言

  • 以下的相關知識總結是通過慕課網的相關學習和自己的相關看法,如果有需要的可以去查看一下慕課網的相關教學,感覺還可以。

爲什麼要使用緩存

  • 一個優點就是讓客戶端下一次的網絡請求節省更多的時間,更快的展示數據

如何開啓和使用緩存功能的呢?

 new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .readTimeout(10000, TimeUnit.MILLISECONDS)
                .writeTimeout(10000, TimeUnit.MICROSECONDS)
                //直接這樣使用  配置Cache   File對象  緩存大小
                .cache(new Cache(new File("cache"),24*1024*1024))
                .build();

Cache.put()方法源碼

@Nullable CacheRequest put(Response response) {
   //獲取到請求方法
    String requestMethod = response.request().method();
    //判斷該請求方法是否符合緩存
    if (HttpMethod.invalidatesCache(response.request().method())) {
      try {
        //移除這個請求
        remove(response.request());
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
      return null;
    }
    //非get方法不需要緩存
    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;
    }
    //
    if (HttpHeaders.hasVaryAll(response)) {
      return null;
    }
    //當經過上述的邏輯之後 到這一步 證明是可以緩存的      //那麼就通過傳入的response響應   創建Entry對象  就是我們需要寫入緩存的   把需要的一些屬性  方法  地址  頭部 信息  code 等
    Entry entry = new Entry(response);
    //可以看到這裏採用的是DiskLruCache緩存策略
    //http://blog.csdn.net/guolin_blog/article/details/28863651  (Android DiskLruCache完全解析,硬盤緩存的最佳方案)
    DiskLruCache.Editor editor = null;
    try {
      //把這個請求url(經過轉換MD5加密然後轉十六進制)作爲key
      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;
    }
  }

entry.writeTo()方法

public void writeTo(DiskLruCache.Editor editor) throws IOException {
      //使用的是okio
      BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));
      //緩存的請求地址
      sink.writeUtf8(url)
          .writeByte('\n');
      //緩存的請求方法
      sink.writeUtf8(requestMethod)
          .writeByte('\n');
      sink.writeDecimalLong(varyHeaders.size())
          .writeByte('\n');
          //對header頭部進行遍歷
      for (int i = 0, size = varyHeaders.size(); i < size; i++) {
      //名字
        sink.writeUtf8(varyHeaders.name(i))
            .writeUtf8(": ")
            //value
            .writeUtf8(varyHeaders.value(i))
            .writeByte('\n');
      }
      //緩存http的響應行StatusLine
      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');
     //判斷是否是https請求
      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();
    }

new CacheRequestImpl(editor)

CacheRequestImpl實現了CacheRequest接口,主要暴漏給後面需要介紹的CacheInterceptor攔截器  緩存攔截器可以直接根據這個CacheRequest實現類 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();
        }
      };
    }

put()總結

  • 1、首先判斷緩存請求方法是否是get方法
  • 2、如果符合緩存策略,那麼創建一個Entry對象—>用於我們所要包裝的緩存信息
  • 3、最終使用DiskLruCache進行緩存
  • 4、最後通過返回一個CacheRequestImpl對象,這個對象主要用於CacheInterceptor攔截器服務的。

Cache.get()方法源碼

主要從緩存中讀取緩存的response

@Nullable Response get(Request request) {
    //通過傳入的Request對象獲取請求地址  並通過key方法獲取緩存的key
    String key = key(request.url());
    //緩存快照  記錄緩存在特定時刻緩存的內容
    DiskLruCache.Snapshot snapshot;

    Entry entry;
    try {
      //通過key值獲取value
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
      //通過獲取到的這個snapshot獲取到Source  最終創建entry對象
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }
   //通過之前創建好的entry實例獲取到我們緩存的response對象
    Response response = entry.response(snapshot);
    //響應和請求是否一一對應  如果不對應 直接返回null
    if (!entry.matches(request, response)) {
      //關閉流
      Util.closeQuietly(response.body());
      return null;
    }
    //最終返回我們緩存的response實例
    return response;
  }

entry.response(snapshot)方法


    public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      //創建request
      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();
    }

get()方法總結

  • 1、根據請求url獲取key(MD5解密)
  • 2、通過key值獲取 DiskLruCache.Snapshot(緩存快照)
  • 3、通過獲取到的這個snapshot獲取到Source 最終創建entry對象
  • 4、通過entry和snapshot(緩存快照)獲取緩存的response並返回

這裏所總結的Cache的get和put的相關知識,主要是爲了爲CacheInterceptor這個緩存攔截器做鋪墊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章