當前位置:
首頁 > 知識 > 技術分享:剖析OkHttp緩存機制

技術分享:剖析OkHttp緩存機制

現在應用市場上的 App 無一不需要網路操作,這些應用的開發者大多數都選擇結合使用 OkHttp 和 Retrofit 來完成網路操作。okHttp 最為人稱道的一個特性就是它的緩存機制,而我將在本篇博文對其進行剖析。

每次我用 OkHttp 時我都需要一些時間想想我將怎麼使用它,我該用哪一個 HTTP 報頭,作為一個客戶端 App 我有哪些職責,我期望從伺服器上獲得什麼,等等……所以對我來說,這篇博文將是 OkHttp 緩存特性的引用文檔,我寫這篇文檔不是因為我在之後的開發中可能會接觸 OkHttp 的緩存機制,而是因為我很有可能在下次需要使用它的時候卻把這些知識忘了。

這篇博文用於幫助熟悉 OkHttp 的高級工程師,我希望這篇博文的讀者最起碼應該知道怎麼使用 OkHttp 以及怎麼開啟緩存。如果你不知道的話,就去 OkHttp 的維基頁面學習吧。當然了,這裡面的解釋可能有一部分是錯的,我只是嘗試去解釋一些我所理解的內容,所以如果你發現我某些部分的解釋是有問題的,請通過郵件或其他方式告訴我。

分析的起點

我將會從以下類開始我的分析工作:CacheStrategy 和 HttpEngine,其中 CacheStrategy 包含緩存機制的所有邏輯,HttpEngine 是 CacheStrategy 被調用的地方。

· CacheStrategy.Factory 理解緩存候選響應的報頭並將其轉換為類成員的構造器

· CacheStrategy.getCandidate() - 檢查緩存候選響應,如果需要的話講修改原始請求的報頭

這兩部分內容是 OkHttp 整個緩存機制最關鍵的部分,而且幾乎包含了所有我們想要了解的信息。雖說其他方法也很重要,但如果你去分析的話會發現最終還是會回到這裡。

什麼是緩存候選響應?

第一次進行 HTTP 請求時不會存在任何已緩存內容,相關 API 只在有緩存內容的時候才會被調用,這點相信不需要我再多解釋了。

一旦響應被存儲我們就能嘗試將它用到後續的調用中,當然了,我們不可能將所有響應碼對應的響應都存儲下來。我們只存儲以下響應碼對應的響應:200, 203, 204, 300, 301, 404, 405, 410, 414, 501, 308。除此以外還有 302, 307,但存儲它們對應的響應時必須滿足以下條件之一:

· contains Expires header OR

· CacheControl contains max-age OR

· CacheControl contains public OR

· CacheControl contains private

· 包含 Expires 報頭

· CacheControl 包含 max-age

· CacheControl 包含 public

· CacheControl 包含 private

需要注意:OkHttp 的緩存機制不支持緩存部分報文內容。

當我們重複某個請求,OkHttp 會判斷是否已經有已緩存的響應,後文中我將稱其為緩存候選響應。

CacheStrategy 是什麼?

CacheStrategy 需要一個新的報文和一個緩存候選響應,評估這兩個 HTTP 報頭是否有效並比較它們。

首先,存放在緩存候選響應報頭中的部分成員是:

· Date

· Expires

· Last-Modified

· ETag

· Age

下面是需要檢查的條件的匯總列表:

1. 判斷緩存候選響應是否存在。

2. 如果接收的是 HTTPS 請求,如果需要的話,判斷緩存候選響應是否已進行握手。

3. 判斷緩存候選響應是否已緩存;這和 OkHttp 存儲響應時完成的工作是相同的。

4. 如果沒有緩存,在請求報頭的 Cache-Control 中檢查對應內容,如果該標記為 true,緩存候選響應將不會被使用,後面的檢查也會跳過。

5. 在請求報頭中查找 If-Modified-Since 或 If-None-Match,如果找到其中之一,緩存候選響應將不會被使用,後面的檢查也會跳過。

6. 進行一些計算以得到諸如緩存響應緩存響應存活時間,緩存存活時間,緩存最大失活時間。我不希望在此解釋所有對應實現的細節,因為這會讓博文變得冗長,你只需要知道以上提到的報文內容(例如:Date, Expires 等等…)還有請求 Cache-Control 中的 max-age, min-fresh, max-stale 這部分相關的計算耗時是毫秒級的。完成檢查最簡單的辦法是寫這樣的偽代碼:if (cache candidate"s no-cache cache candidate"s age request"s min-fresh cache candidate"s fresh lifetime request"s max-stale)

如果上述條件被滿足,那麼已緩存的響應報文將會被使用,後面的檢查將跳過。 7. 在需要進行網路操作時,下一次檢查會判斷它是否為「條件請求」。條件請求指的是:發送報文請求伺服器響應,而伺服器可能會也可能不會返回新的內容,或讓我們使用已緩存的報文。

WE LOOK FOR ANDROID DEVELOPERS!

條件請求

下一節看起來是新的內容,但在代碼結構中和上一節所解釋內容是在相同的方法里的。

1. 如果緩存候選響應包含 ETag 報頭,那麼新的 If-None-Match 請求報文會使用相同的 ETag 值。

2. 如果上一點沒有被滿足,且緩存候選響應包含 Last-Modified,那麼新的 If-Modified-Since 請求報文會使用該值。

3. 如果以上兩點都沒有被滿足,且緩存候選響應包含 Date,那麼新的 If-Modified-Since 請求報文會使用該值。

還有更多!

上述描述過程的最後,也就是 CacheStrategy.getCandidate() 方法返回後,還會有一個檢查。OkHttp 會在此時判斷報頭的Cache-Strategy 是否包含 「only-if-cached「 參數。如果有的話,不會請求新的數據。OkHttp 會強制使用緩存候選響應(如果有且可用),不然的話會拋出 HTTP 504 Unsatisfiable Request 錯誤。

怎麼運作?

你可能自己已經知道,進行 HTTP 操作時就是設置正確的報頭。但不添加額外的報頭內容,使用默認的 HTTP 調用也能完成網路操作,我會在後面提及這部分內容。

無網路環境

剛開始我遇到的問題就包含這個,那時我天真地以為只要允許進行緩存就會解決這個問題,然而並不行,我知道的兩種解決方法是:

· 使用 Cache-Control : only-if-cached header,報文將永遠也不會到達伺服器。只有存在可用緩存響應時才會檢查並返回它。不然的話,會拋出 504 錯誤,所以開發的時候別忘了處理這個異常。

· 使用 Cache-Control : max-stale=[seconds] 報頭,這種辦法更靈活一些,它向客戶端表明可以接收緩存響應,只要該緩存響應沒有超出其生命周期。而該緩存響應是否可用由 OkHttp 檢查,所以不需要與伺服器建立連接來判斷。但即使如此,當緩存響應超出其生命周期,網路操作還是會進行,然後得到伺服器返回的新內容。

關心用戶的開銷和運行效率

上面提到的解決方法依賴 OkHttp 的驗證機制能在離線時完成一些操作。通常 App 都是在線狀態,而我們更希望得到新的數據,這樣會一次又一次地從伺服器上獲取相同的內容,給用戶產生冗餘的開銷並降低 App 的運行效率。

理想狀態下,我們發送一個請求,得到真正的新數據。當伺服器的數據沒有發生改變,將得到 304 響應碼,而 OkHttp 返回緩存響應。如果伺服器支持緩存的話,這種情況是可以實現的。所以你需要做的大體就是和你的同事研究 ETag 或 Last-Modified-Since 是否被支持,如果支持的話,OkHttp 會幫你解決剩下的麻煩!

NOTE: 請不要自己設置 If-Modified-Since 或 If-None-Match。前文已經提到,設置這兩個報頭會跳過後續的許多檢查,進行網路操作。

強制網路操作

你也可以強制進行網路操作,但就我個人而言,我還沒有這樣用過 OkHttp,不過我發現了一些可能的應用場景。例如你在報頭中使用了 max-stale,過了很久伺服器接收到了該報文。當你想允許用戶在某些特殊情況下強行進行刷新時,很簡單,在請求中使用 Cache-Control : no-cache,你就能從伺服器得到新的數據了。

結論

在我看來,OkHttp 非常易用,極大簡化了網路操作。而且有助於理解數據流,哪些報頭能被設置以及它們的用處。我希望這篇博文能給你提供一些幫助。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 千鋒JAVA開發學院 的精彩文章:

Kotlin技術分享-運算符重載
重學計算機組成原理-「燙燙燙」亂碼的由來

TAG:千鋒JAVA開發學院 |