glide使用緩存
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. Android 【手撕Glide】--Glide緩存機制(面試)
本文源碼解析基於Glide 4.6.1
系列文章
Android 【手撕Glide】--Glide緩存機制
Android 【手撕Glide】--Glide緩存機制(面試)
Android 【手撕Glide】--Glide是如何關聯生命周期的?
Glide緩存分為內存緩存和磁碟緩存,其中內存緩存是由弱引用+LruCache組成。
取的順序是:弱引用、LruCache、磁碟
存的順序是:磁碟、弱引用、LruCache
這張親手製作的圖片,方便大家更直觀的理解緩存機制的整體流程,結合文末總結效果更佳。喜歡的記得點贊!
概述
1、弱引用是由這樣一個HashMap維護,key是緩存的key,這個key由圖片url、width、height等10來個參數組成;value是圖片資源對象的弱引用形式。
2、LruCache是由一個LinkedHashMap維護,根據Lru演算法來管理圖片。大致的原理是利用linkHashMap鏈表的特性,把最近使用過的文件插入到列表頭部,沒使用的圖片放在尾部;然後當圖片大小到達預先設置的一個閥值的時候 ,按演算法刪除列表尾部的部分數據。由於篇幅有限,這里不講解LruCache和DiskLruCache的底層原理,這里推薦一篇 圖解LinkedHashMap原理
這是Glide自定義的LruCache
存取原理
取數據
在內存緩存中有一個概念叫圖片引用計數器 ,具體來說是在 EngineResource 中定義一個 acquired 變數用來記錄圖襪指正片被引用的次數,調用 acquire() 方法會讓變數加1,調用 release() 方法會讓變數減1。
獲取圖片資源是先從弱引用取緩存,拿到的話,引用計數+1;沒有的話從LruCache中拿緩存,拿到的話,引用計數逗枝也是+1,同時把圖片從LruCache緩存轉移到弱應用緩存池中;再沒有的話就通過 EngineJob 開啟線程池去載入圖片,拿到的話,引用計數也是+1,會把圖片放到弱引用。
存數據
很明顯,這是載入圖片之後的事情。通過 EngineJob 開啟線程池去載入圖片,取到數據之後,會回調到主線程,把圖片存到弱引用。當圖片不再使用的時候,比如說暫停請求或者載入完畢或者清除資源時,就會將其從弱引用中轉移到告悔 LruCache 緩存池中。 總結一下,就是正在使用中的圖片使用 弱引用 來進行緩存,暫時不用的圖片使用 LruCache 來進行緩存的功能;同一張圖片只會出現在 弱引用 和 LruCache 中的一個。
為什麼要引入軟引用?
1、分壓策略,減少Lrucache 中 trimToSize 的概率。如果正在remove的是張大圖,lrucache正好處在臨界點,此時remove操作,將延緩Lrucache的 trimToSize 操作;
2 提高效率:弱引用用的是 HashMap ,Lrucache用的是 LinkedHashMap ,從訪問效率而言,肯定是 HashMap 更高。
Glide磁碟緩存策略(4.x)
如果在內存緩存中沒獲取到數據會通過 EngineJob 開啟線程池去載入圖片,這里有2個關鍵類: DecodeJob 和 EngineJob 。 EngineJob 內部維護了線程池,用來管理資源載入,當資源載入完畢的時候通知回調; DecodeJob 是線程池中的一個任務。
磁碟緩存是通過 DiskLruCache 來管理的,根據緩存策略,會有2種類型的圖片, DATA (原始圖片)和 RESOURCE (轉換後的圖片)。磁碟緩存依次通過 ResourcesCacheGenerator 、 SourceGenerator 、 DataCacheGenerator 來獲取緩存數據。 ResourcesCacheGenerator 獲取的是轉換過的緩存數據; SourceGenerator 獲取的是未經轉換的原始的緩存數據; DataCacheGenerator 是通過網路獲取圖片數據再按照按照緩存策略的不同去緩存不同的圖片到磁碟上。
Glide緩存分為 弱引用+ LruCache+ DiskLruCache ,其中讀取數據的順序是:弱引用 > LruCache > DiskLruCache>網路;寫入緩存的順序是:網路 --> DiskLruCache--> LruCache-->弱引用
內存緩存分為弱引用的和 LruCache ,其中正在使用的圖片使用弱引用緩存,暫時不使用的圖片用 LruCache緩存,這一點是通過 圖片引用計數器(acquired變數)來實現的,詳情可以看內存緩存的小結。
磁碟緩存就是通過DiskLruCache實現的,根據緩存策略的不同會獲取到不同類型的緩存圖片。它的邏輯是:先從轉換後的緩存中取;沒有的話再從原始的(沒有轉換過的)緩存中拿數據;再沒有的話就從網路載入圖片數據,獲取到數據之後,再依次緩存到磁碟和弱引用。
參考:
面試官:簡歷上最好不要寫Glide,不是問源碼那麼簡單
原來面試的時候寫精通Glide,這樣問我這樣答
C. 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 的封裝;
D. Glide使用
佔位符和漸現動畫
Glide 的流式介面只需要調用 .placeHolder()用一個 drawable(resource) 引用,Glide 將會顯示它作為一個佔位符,直到你的實際圖片准備好。
不能設置一個網路 url 作為佔位符,因為這也會被去請求載入的。App 資源和 drawable 能保證可用和可訪問的。然而,作為 load()方法的參數,Glide 接受所有值。
當App 嘗試從一個網站去載入一張圖片,但由於某些原因載入失敗,使用錯誤佔位符:.error(),在大多數情況下使用佔位符,來指明圖片不能被載入已經足夠了。
error()接受的參數只能是已經初始化的 drawable 對象或者指明它的資源。
Glide 使用標準的淡入淡出動畫,這是默認激活的。如果你想要如強制 Glide 顯示一個淡入淡出動畫,你必須調用另外一個建造者:
crossFade()方法還有另外重載方法 .crossFade(int ration)。如果你想要去減慢(或加快)動畫,隨時可以傳一個毫秒的時間給這個方法。動畫默認的持續時間是 300毫秒。
用 resize(x,y) 調整圖片大小
Glide 自動限制了圖片的尺寸在緩存和內存中,並給到 ImageView需要的尺寸。如果圖片不會自動適配到 ImageView,調用 override(horizontalSize, verticalSize) 。這將在圖片顯示到 ImageView之前重新改變圖片大小。
當你還沒有目標 view 去知道尺寸的時候,這個選項也可能是有用的。比如,如果 App 想要在閃屏界面預熱緩存,它還不能測量 ImageView的尺寸。然而,如果你知道這個圖片多少大,用 override 去提供明確的尺寸。
縮放圖像
顯示 Gif
檢查圖片載入的是否是一個gif圖片,調用一個額外的防區強制 Glide變成一個 Gif asGif()
如果這個 gifUrl 不是一個 Gif,.error()回調被調用並且錯誤佔位符被顯示。
Gif 轉為 Bitmap
如果你僅僅想要顯示 Gif 的第一幀,你可以調用 asBitmap()去保證其作為一個常規的圖片顯示,即使這個 URL 是一個 Gif。
內存緩存
Glide 通過使用默認的內存和磁環緩存去避免不必要的網路請求。調用了 .skipMemoryCache(true)去明確告訴 Glide 跳過內存緩存。可以用 .diskCacheStrategy()方法為 Glide 改變磁碟緩存的行為,如果要為一個請求禁用磁碟緩存。使用枚舉 DiskCacheStrategy.NONE。
作為參數。
Glide 緩存了原始圖像,全解析度圖像和另外小版本的圖像。對於 .diskCacheStrategy()方法來說不同的枚舉參數的意義:
如果有一張圖片,將會經常操作處理,並做了一堆不同的版本,對其有意義的僅僅是緩存原始解析度圖片,用 DiskCacheStrategy.SOURCE。
圖片請求的優先順序
Priority (優先順序)枚舉
你正在實現一個信息詳情頁面,有一個英雄圖片在頂部,和較小的圖片在底部。對於最好的用戶體驗來說,英雄圖片首先需要被載入。因此,我們用 Priority.HIGH
來處理它。理論上說,這應該夠了,但是為了讓這個實例增加點趣味,我們也將底層圖像分配給低優先順序,用 .priority(Priority.LOW)
調用:
縮略圖
用原圖的1/10作為縮略圖
用其它圖片作為縮略圖
Glide 中的回調:Targets
Glide 提供了一個用 Targets的簡單的方式去接受圖片資源的 Bitmap。Targets 是沒有任何別的回調,它在 Glide 做完所有的載入和處理之後返回結果。
不能使用常規的 Glide 的方法 .into(),因為我們的自定義 view 並不繼承自 ImageView。因此,我們必須創建一個 ViewTarget,並用 .into()方法:
創建一個自定義通知
上述創建了三個重要的對象,notification和 RemoteViews以及常量 NOTIFICATION_ID。利用這些去創建一個通知 target。
調用 Glide,將 target 作為 .into()的參數。
Transformations(轉換)
在圖片被顯示之前,transformations(轉換) 可以被用於圖像的操作處理。圖片的任意屬性:顏色、尺寸、范圍、顏色、像素位置等等。下面的庫它為 Glide 轉換提供了多種多樣的實現:
glide-transformations
用 animate() 自定義動畫
創建自己的 XML 動畫,比如一個小的縮放動畫,圖片剛開始小的,然後逐漸增大到原尺寸。
這兩個動畫可以添加到Glid中
在 AndroidManifest.xml的 <application>標簽內去聲明這個剛剛創建的 Glide mole。
android:name屬性是包名+類名的形式。
要看第一個方法applyOptions(Context context, GlideBuilder builder),可以在這個方法里去調 GlideBuilder中可用的方法。
參考:
Glide系列教程