天天看點

Volley(六 )—— 從源碼帶看Volley的緩存機制

它由兩部分組成,一部分是頭部,一部分是内容;先得從它的内部靜态類CacheHeader(緩存的頭部資訊)講起,先看它的内部結構:

<a></a>

可以看到,Entry裡面和CacheHeader裡有四個參數是一樣的,隻是Entry裡多了data[],data[]就是用來儲存主要資料 的。看到這你可以有點迷糊,Entry和CacheHeader裡為什麼要有四個參數一樣,先簡單說一下原因:volley架構裡都用到接口程式設計,是以實 際代碼中除了初始化,你隻看到cache,而DiskBasedCache是看不到的,是以必須在Entry裡先把那些緩存需要用到的參數保留起來,然後 具體實作和封裝放在DiskBasedCache裡。

初始化

DiskBasedCache的初始化時在RequestQueue建立時就發生的,可以看Volley.newRequestQueue()的源碼:

可以看到,磁盤緩存的路徑為:context.getCacheDir(),如果maxDiskCacheBytes有傳入,就以傳入的為準,如果為空:

磁盤預設為5M。是以如果你想設定最大的磁盤緩存值,那麼就不能直接向下面那樣這樣初始化了:

而是需要這樣:

存放緩存資料

第一次緩存的資料是從哪來的呢,當然是從網上來,看NetWorkDispatcher的run方法裡:

其中request.getCacheKey()預設為請求的url,response.cacheEntry是Cache.Entry,裡面已存 放好解析完的httpResponse資料,request.shouldCache()預設是需要緩存,如果不需要可調用 request.setShouldCache(false)來去掉緩存功能。

我們把請求和處理的http的傳回略過,留下幾行關鍵代碼,如果這個請求需要緩存(預設需要)和緩存資訊不為空,那麼就儲存緩存資訊。接下來看,DiskBaseCache是怎麼儲存緩存的:

可以看到,每次寫入緩存之前,都先調用pruneIfNeeded()檢查對象的大小,當緩沖區空間足夠新對象的加入時就直接添加進來,否則會删除 部分對象,一直到新對象添加進來後還會有10%的空間剩餘時為止,檔案引用以LinkHashMap儲存。添加時,首先以URL為key,經過個文本轉換 後,以轉換後的文本為名稱,擷取一個file對象。首先向這個對象寫入緩存的頭檔案,然後是真正有用的網絡傳回資料。最後是目前記憶體占有量數值的更新,這 裡需要注意的是真實資料被寫入磁盤檔案後,在記憶體中維護的應用,存的隻是資料的相關屬性。

從緩存資料裡取緩存

我們知道隊列建立後就會有一個緩存線程在背景一直運作等待着緩存請求進來,但在等待線程前,會先調用mCache.initialize(),把緩存資料的頭部資訊放進一個Map類型mEntries裡,這樣以後要用到就先用mEntries判斷,速度更快。

如果請求進來即調用<code>Cache.Entry entry = mCache.get(request.getCacheKey())</code>,那我們就看DiskBaseCache。get方法裡做了什麼:

從方法裡可以看到,先從檔案裡獲得位元組數輸入流,從中減去頭部檔案的位元組數,最後把真正内容的data[]資料拿到再組裝成一個Cache.Entry傳回。不得不說,Volley這真是精打細算啊。

從上面的分析可見,cache在做一些基礎判斷時都會先用到緩存的頭部資料,如果确定頭部資訊沒問題了,再真正讀寫内容,原因是頭部資料比較小,放在記憶體中也不占地方,但處理速度會快很多。而真正的資料内容,可能會比較大,處理的開銷也大,隻在真正需要的地方讀寫。

http的304狀态碼的含義是:

如果伺服器端的資源沒有變化,則自動傳回 HTTP 304 (Not Changed.)狀态碼,内容為空,這樣就節省了傳輸資料量。當伺服器端代碼發生改變或者重新開機伺服器時,則重新發出資源,傳回和第一次請求時類似。進而 保證不向用戶端重複發出資源,也保證當伺服器有變化時,用戶端能夠得到最新的資源。

完整的過程如下:

用戶端請求一個頁面(A)。

伺服器傳回頁面A,并在給A加上一個Last-Modified/ETag。(Last-Modified為标記此檔案在服務期端最後被修改的時間,ETag是這個請求的token)

用戶端展現該頁面,并将頁面連同Last-Modified/ETag一起緩存。

客戶再次請求頁面A,并将上次請求時伺服器傳回的Last-Modified/ETag一起傳遞給伺服器。

伺服器檢查該Last-Modified或ETag,并判斷出該頁面自上次用戶端請求之後還未被修改,直接返 回響應304和一個空的響應體。

介紹完304,我們接下來來看看volley是怎麼運用304來重用緩存的。

首先我們來看一下對于response.header的處理,在每一個request裡,都必須繼承 parseNetworkResponse(NetworkResponse response)方法,然後在裡面用 HttpHeaderParser.parseCacheHeaders()解析類來解析頭部資料,具體如下:

從上面代碼可以看出緩存頭部是根據 Cache-Control 和 Expires 首部,計算出緩存的過期時間(ttl),和緩存的新鮮度時間(softTtl,預設softTtl和ttl相同),如果有Cache-Control标簽 以它為準,沒有就以Expires标簽裡的内容為準。

需要注意的是:Volley沒有處理Last-Modify首部,而是處理存儲了Date首部,并在後續的新鮮度驗證時,使用Date來建構If-Modified-Since。 這與 Http 1.1 的語義有些違背。

在使用緩存資料前,Volley會先對驗證緩存資料是否過期,是否需要更新等屬性,然後一一處理,代碼在CacheDispatcher的run方法裡:

上面代碼都已經加了注釋,相信不難了解,那我們繼續看,網絡請求是怎麼判斷是否需要更新緩存的,在BasicNetwork.performRequest()裡:

從上面的注釋可以看到,如果是傳回304就直接用緩存資料傳回。那來看NetworkDispatcher的run()裡:

現在流程比較清晰了,在有緩存的情況下,如果已經過期,但是傳回304,就複用緩存。如果不新鮮了,就先将緩存分發出去,然後再進行網絡請求,看是否需要更新緩存。

不過眼尖的讀者一定有個疑惑,在解析頭部資料時,預設不是新鮮度和過期事件是一樣的嗎?那新鮮度不是一定運作不到嗎?确實是這樣,我也有這個疑惑, 網上也找不到确切的資料來解釋這一點。不過按照正常的邏輯,新鮮度時間一定比過期時間短,這樣我們就可以根據實際需要更改Volley的源碼。例如,我們 可以直接把新鮮度的驗證時間設為3分鐘,而過期時間設為一天,代碼如下:

然後使用的時候:

這樣的話,在3分鐘後就不新鮮,24小時後就會過期。

我們使用ImageLoader時會傳入一個ImageCache,它是個接口,裡面定義了兩個方法:

那他們是什麼時候使用的呢,可以從開始請求資料ImageLoader.get()方法看起:

從上面的代碼注釋中已經能比較清晰的看出,每次調用ImageLoader.get()方法,會先從記憶體緩存裡先看有沒有資料,有就直接傳回,沒有 就走正常的網絡流程,先檢視磁盤緩存,不存在或過期再去請求網絡。圖檔比普通資料多一層緩存的原因也很簡單,因為圖檔較大,讀取和網絡成本都大,能用緩存 就用緩存,能省一點是一點。

下面來看看具體的流程圖

Volley(六 )—— 從源碼帶看Volley的緩存機制

以上就是Volley架構所使用到的所有緩存機制,如有遺漏請留言指出,多謝閱讀。

參考連結: 

    本文轉自 一點點征服   部落格園部落格,原文連結:http://www.cnblogs.com/ldq2016/p/5195497.html,如需轉載請自行聯系原作者