A. Glide圖片載入的用法介紹和三級緩存實現
Glide庫是用來實現圖片載入的框架,功能強大且易使用,深受大家喜愛。
為啥要做緩存? android默認給每個應用只分配16M的內存,所以如果載入過多的圖片,為了 防止內存溢出 ,應該將圖片緩存起來。
圖片的三級緩存分別是:
1、內存緩存
2、本地緩存
3、網路緩存
其中,內存緩存應優先載入,它速度最快;本地緩存次優先載入,它速度也快;網路緩存不應該優先載入,它走網路,速度慢且耗流量。
最優-優先順序:內存緩存 > 本地緩存 > 網路緩存
兩個方法實現:根據圖片的url去載入圖片、在本地和內存中緩存
兩個方法實現:設置本地緩存,以及獲取本地緩存
兩個方法實現:設置內存緩存,獲取內存緩存。
如果使用hashmap去存儲圖片時,當圖片越來越多,那麼會造成內存溢出,因為是強引用(對於強引用的系統不會回收)
如果改成軟引用softReference,在android 2.3 以上的系統,對象會被提前回收。
可以用LruCache來解決上述內存不回收或提前回收的問題。least recentlly use 最少最近使用演算法 它會將內存控制在一定的大小內, 超出最大值時會自動回收, 這個最大值開發者自己定。(這個東西沒有用過..)
參考鏈接: https://blog.csdn.net/sinat_20645961/article/details/46325243
B. Glide ② — 緩存機制
閱讀本文需要先了解 Glide載入流程
首先介紹一下Glide中對圖片資源的封裝類: EngineResource
在活動緩存中,使用了一個map用來存放EngineResource對象,這里需要注意一個操作,就是這個EngineResource對象是用WeakReference包裹的,並且通過ReferenceQueue監聽了EngineResource的回收,在回收的時候會清理當前的活動緩存內容;
下面分析一下源碼是如果實現的:
首先,自定義一個WeakReference類,將key和resource傳進入(用於在WeakReference回收的時候釋放),傳入一個ReferenceQueue對象,用於監聽WeakReference回收
開啟一個子線程,在循環中監聽ReferenceQueue的返回值,通過這個返回值,判斷WeakReference有沒有回收,監聽的方法是ReferenceQueue.remove(),這是一個阻塞方法;所以要開子線程;
LruResourceCache繼承了LruCache類,關於LruCache類,簡單提一下,具體的可以參考我之前的博客 LruCache實現 ,LruCache繼承了LinkedHashMap,LinkedHashMap有一個特點,就get後的數據會移動到隊列,這就是Lru思想:固定一個容量,put的時候如果超過容量了,將最後一個節點刪除,get的時候將get的這個節點移動到隊列的頭部;
onItemEvicted()方法是LruCache的一個空方法,調用的時機是在put的時候判斷是否超過容量,如果超過容量了,就淘汰最後一個節點,並調用這個方法;
活動緩存和內存緩存都是緩存在內存中的,活動緩存緩存的是正在使用的圖片資源,當圖片不使用時會放到內存緩存中,提出活動緩存的目的:單一的內存緩存由於Lru的淘汰機制會導致圖片載入不穩定
首先介紹一個磁碟緩存方案DiskLruCache(非Google官方編寫,但獲得官方認證),關於這個磁碟緩存方案的理解可以看郭林的這片文章:
Android DiskLruCache完全解析,硬碟緩存的最佳方案
從上一篇文章知道,Glide載入操作是通過 Engine 來驅動的
Engine的load()中,首先嘗試從 活動緩存 和 內存緩存 獲取緩存,如果沒有緩存再啟動EngineJob和DecodeJob; 上面介紹了緩存的獲取,下面看一下緩存的存放,肯定是在獲取到圖片後的回調中存放的
在DecodeJob獲取到圖片數據後,會回調很多介面,在回調中會將其放入 活動緩存 ,當圖片不在使用的時候,就會放入內存緩存,根據上面介紹的活動緩存規則,當 EngineResource 計數為0時就應該放入內存緩存;
當資源引用為0,回調onResourceReleased(),從活動緩存移除,放入內存緩存;
上面介紹了活動緩存和內存緩存的存放和獲取,下面看一看磁碟緩存的存取;
還記得 DataFetcherGenerator 介面嗎?這個介面是DecodeJob用於獲取數據的,有三個具體的實現:
我們在上一篇具體介紹的是網路文件的獲取,這里的磁碟緩存使用的就是 DataCacheGenerator(緩存文件) 這個Generator了
上篇文章知道DecodeJob是一個Runnable任務,在run()會調用runWrapped(),在runWrapped()中會做三種事情:
在runWrapped()的解碼操作中會執行decode(),在decode()中,會disk put操作;
Glide的磁碟緩存是基於DiskLruCache 實現的,Glide直接使用的是DiskLruCacheWrapper對象對DiskLruCache 的封裝;
C. glidecache是什麼文件夾
glidecache是圖片緩存的文件夾,該文件夾可以刪除,但是刪除的同時會刪除手機中的緩存圖片,通過刪除此文件夾刪除的緩存圖片無法找回,一旦刪除無法恢復。
手機緩存是數據交換的緩沖區,緩存是CPU的一部分,它存在於CPU中,而CPU存取數據的速度則非常的快,一秒鍾能夠存取,處理十億條指令和數據。
而內存就慢很多,緩存是為了解決CPU速度和內存速度的速度差異問題,在打開文件時,系統會將數據從內存中復制到一個緩沖區而再打開文件時,系統會直接讀取緩存中的數據,則不用到內存中讀取, 這樣瀏覽文件的速度會比較快。
D. Glide 圖片庫原理(三)緩存機制
查找緩存使用
用完移除
源碼中查看EngineKey-----相當於key,演算法序列abcdsxxfaskldfjklf...
源碼中查看Source-----相當於value(Bitmap),調用系統轉換成Bitmap
情景: 相冊類的App經常需要同時展示大量的圖片,這種情況下圖片的質量可以低一點,因為載入速度優先於圖片的質量。
解決辦法: 我們可以設置解碼的格式,在RequestOptions中加入.encodeFormat(Bitmap.CompressFormat.WEBP).encodeQuality(10))的選項,①encodeFormat的參數有Bitmap.CompressFormat.PNG,Bitmap.CompressFormat.JPEG,Bitmap.CompressFormat.WEBP(質量從高到低);②encodeQuality設置的是0-100的int類型,一個質量百分比參數,越小質量越低。
情景: 大體的意思應該是同一個URL在不同的時間可能會指向不同的資源,所以同樣需要實時更新。
解決辦法相同
情景: 開發一款有頭像的APP,我們修改了頭像並且更新到了服務端,可是當我們點擊查看大圖時載入出來的還是原來的頭像。
解決辦法: 這是Glide強大的緩存帶來的副作用,我們可以在RequestOptions中加入.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)的選項。那麼緩存的功能就會全部關閉,從而使得每次都是從服務端載入,所以頭像會是最新。
情景: 省流量模式的應用情景就是減少不必要圖片的載入。
解決辦法: 我們可以在RequestOptions中加入onlyRetrieveFromCache(true)的選項。那麼圖片就只會從緩存中讀取,如果沒有緩存則不載入圖片,從而達到減少流量消耗的目的。
E. Android-Glide的緩存機制
很多小夥伴都在使用 Glide 載入圖片,出去面試的時候肯定會被問蔽備起,「Glide緩存機制,你了解多少?」。這篇博客我來說說我了解到的 Glide 的緩存機制。
默認情況下,Glide 會在開始一個新的圖片請求之前檢查以下多級的緩存:
1.活動資源 (Active Resources) - 現在是否有另一個 View 正在展示這張圖片?
2.內存緩存 (Memory cache) - 該圖片是否最近被載入過並仍存在於內存中?
3.資源類型(Resource) - 該圖片是否之前曾被解碼緩讓、轉換擾並局並寫入過磁碟緩存?
4.數據來源 (Data) - 構建這個圖片的資源是否之前曾被寫入過文件緩存?
設置內存緩存開關:
設置磁碟緩存模式:
可以設置4種模式:
F. Glide獲取圖片緩存文件名Key
最近項目由於需要支持gif動圖,所以把圖片載入框架由 ImageLoader 切換到 glide ,因為需要支持長按保存圖片,所以就需要找到 glide 加橋搭帆載後緩存的圖片路徑。
根據網上資料,最終找到 Glide 最終生成path的路徑為: /data/枝沒data/your_packagexxxxxxx/cache/image_manager_disk_cache
對應的生成規則: com.bumptech.glide.load.engine.EngineKey#updateDiskCacheKey
問題轉換為:獲取url到緩存文件path的生成規則演算法上
直接把獲取函數貼出敏雹來:
G. Glide圖片緩存策略
Glide四級緩存:
先找內存,再找文件
1)活動緩存(活動資源):ActiveResource,里邊使用一個弱衫悶引用weakHashMap來保存正在使用的圖片,當我們載入圖殲襪片的時候,先從activeResource里邊去查找,如果找不到的話就從內存緩存里查找。理論上沒有大小限制,但是因為是弱引用管理的,所以是可回收的。 存活於內存當中,非持久化
2)內存緩存:默認使用的是LRU的memoryCache,如果沒有找到,將從文件緩存中查找。 存活於內存當中,非持久化。
3)文件緩存:先從資源類型文件里查找(經過了圓角縮放處理的),然後從數據來源文件(未經過處理)里查找, 存活於磁碟中,持久化
這種框架的基本思路:源碼的三條主線
(1)請求是怎樣發送的?
(2)請求是怎樣處理的?
(3)或改彎請求是怎樣維護的?
Glide.with(context) ---->構建一個RequestManager
.load() ------------->輸入數據模型,創建一個RequestBuilder,建造者模式
.into()------->調用RequestBuilder的load方法,buildRequest方法,傳入target,