上篇文章整理了一下 ImageView 直接展示圖片相關(guān)流程吠冤,這次再寫(xiě)一篇關(guān)于第三方圖片加載框架 Glide 的總結(jié)。很早之前就已經(jīng)看郭大神的博客研究了一把 Glide 昼捍,把相關(guān)的圖都整理了一遍基公,但是還未寫(xiě)文章,實(shí)話說(shuō)骗随,用心寫(xiě)好一篇文章總結(jié)蛤织,真的好花時(shí)間。
以下以我自身研究所得進(jìn)行整理鸿染,主在總結(jié)以及記錄一些刷新自己認(rèn)知的點(diǎn)指蚜。
基本用法
Glide 的使用方式非常簡(jiǎn)單,只需要一句代碼:
Glide.with(this).load(url).into(imageView);
這是我們常用的加載圖片姿勢(shì)涨椒,最關(guān)鍵的三步摊鸡,先 with() 確定圖片加載的生命周期,再 load() 加載不同類型的圖片資源蚕冬,最后 into() 展示到目標(biāo)View柱宦。這么一句簡(jiǎn)單的代碼,底層實(shí)現(xiàn)可是復(fù)雜得很播瞳,要整個(gè)都弄清楚,需要花費(fèi)很長(zhǎng)時(shí)間免糕,所以只沿一條主線開(kāi)始赢乓。
Glide 提供了豐富的API,功能強(qiáng)大石窑,整理如下:
- 支持加載各種各樣的圖片資源牌芋,包括網(wǎng)絡(luò)圖片、本地圖片松逊、應(yīng)用資源躺屁、二進(jìn)制流、Uri對(duì)象等等经宏,load() 方法有多個(gè)方法重載犀暑;
- 支持設(shè)置圖片加載前展示臨時(shí)占位圖,placeholder() 方法烁兰,以及圖片加載錯(cuò)誤時(shí)展示異常占位圖耐亏,error() 方法;
- 支持靜態(tài)圖片和 gif 圖片格式沪斟,通過(guò) asBitmap() 或 asGif() 方法指定加載哪種格式广辰;
- 支持設(shè)置加載圖片的大小,比如網(wǎng)絡(luò)圖片下載之前設(shè)置圖片大小為View尺寸大小主之,然后加載壓縮后的大小到內(nèi)存择吊,避免大圖OOM,override() 方法槽奕;
- 支持加載網(wǎng)絡(luò)圖片時(shí)各種緩存策略几睛,diskCacheStrategy() 方法;
- 支持不同 ViewController 中圖片加載的生命周期,with() 方法史翘。
實(shí)現(xiàn)流程
Glide 的完整使用姿勢(shì)枉长,為鏈?zhǔn)浇Y(jié)構(gòu):
Glide.with(activity) // RequestManager
.load("") // DrawableTypeRequest
.asBitmap() // BitmapTypeRequest
.placeholder(0) // BitmapRequestBuilder<String, Bitmap>
.diskCacheStrategy(DiskCacheStrategy.ALL) // BitmapRequestBuilder
.into(imageView); // Target<Bitmap>
依照 with() -> load() -> intot() 這三步冀续,源碼中相關(guān)的類有這么幾個(gè):
- Glide:?jiǎn)卫悾脕?lái)構(gòu)建 RequestBuilder必峰,并且持有 Engine洪唐,BitmapPool,DiskCache吼蚁,MemoryCache 實(shí)例
- RequestManager:管理&開(kāi)始請(qǐng)求加載圖片凭需,該類實(shí)現(xiàn)了 LifecycleListener 生命周期接口,所以能根據(jù)Activity 和 Fragment 的生命周期決定 stop肝匆,start 以及 restart 圖片的加載粒蜈;
- GenericRequestBuilder:A generic class that can handle setting options and staring loads for generic resource types.
- Target:An interface that Glide can load a resource into and notify of relevant lifecycle events during a load.
用一句簡(jiǎn)單的話來(lái)概括這幾個(gè)類之間的關(guān)系就是:Glide負(fù)責(zé)構(gòu)建含有生命周期的 RequestManager枯怖,RequestManager 根據(jù)load加載的不通圖片資源構(gòu)建具體的 ReuestBuilder,當(dāng)請(qǐng)求的圖片資源按照設(shè)置的規(guī)則(壓縮能曾、縮放度硝、緩存等)處理后,再回調(diào)給 Target寿冕,具體是通過(guò)何種 Target 子類設(shè)置蕊程,則根據(jù)不同的資源類型來(lái)確定。
注:類圖上方 RequestBuilder 和 下方 ImageViewTarget 子類相對(duì)應(yīng)驼唱,GlideDraeablrImageViewTarget 支持動(dòng)畫(huà)的加載藻茂,所以對(duì)應(yīng)為 GifRequestBuilder 請(qǐng)求加載的 gif 資源。
緩存策略
緩存在請(qǐng)求網(wǎng)絡(luò)圖片時(shí)能減少不必要的流量浪費(fèi)玫恳。Glide 緩存分為內(nèi)存緩存和硬盤(pán)緩存辨赐,這兩個(gè)緩存模塊的作用各不相同,內(nèi)存緩存的主要作用是 防止應(yīng)用重復(fù)的將圖片數(shù)據(jù)讀取到內(nèi)存京办,而硬盤(pán)緩存則是防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方下載和讀取數(shù)據(jù)肖油。
關(guān)于緩存,就一定涉及到緩存 key 臂港。如何生成 key 呢森枪?查看 KeyFactory.buildKey() 方法。一般一張網(wǎng)絡(luò)圖片的唯一標(biāo)志是圖片 url 地址审孽,但是基于這張圖片加載時(shí)設(shè)置的一系列規(guī)則县袱,比如 override 設(shè)置了固定大小也會(huì)生成新的緩存 Key .
內(nèi)存緩存
Glide 默認(rèn)是開(kāi)啟了內(nèi)存緩存,可以通過(guò)代碼設(shè)置:
Glide.with(this).load(url).skipMemoryCache(true).into(iv);
skipMemoryCache() 方法傳入 true佑力,就表示禁止 Glide 的內(nèi)存功能式散。
內(nèi)存緩存經(jīng)常會(huì)用到 LruCache 算法,Glide 的內(nèi)存緩存用到的也是 LruCache 算法打颤,同時(shí)也用到了一種弱引用機(jī)制暴拄,共同完成內(nèi)存緩存漓滔。
追溯源碼至 Engine.load() 方法,這一步開(kāi)始決定是使用緩存數(shù)據(jù)還是直接重新下載圖片乖篷,整個(gè)流程如下:這里有個(gè)問(wèn)題:LruCache 只看到了取數(shù)據(jù)的過(guò)程响驴,那么何時(shí)會(huì)存數(shù)據(jù)呢?
A: Engine.onResourceReleased() 釋放資源時(shí)撕蔼,具體在于 acquire 引用計(jì)數(shù)器的作用:
- 當(dāng)計(jì)數(shù)器>0豁鲤,說(shuō)明圖片正在使用,存在于 activeResources 弱引用map中鲸沮;
- 經(jīng)過(guò) release() 后琳骡,若計(jì)數(shù)器=0,說(shuō)明圖片不再被使用讼溺,就會(huì)調(diào)用上述方法釋放資源楣号,此時(shí)在該方法中會(huì)將當(dāng)前沒(méi)有使用的資源緩存到 LruCache 中。
磁盤(pán)緩存
Glide 禁止磁盤(pán)緩存的代碼如下:
Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(iv);
先來(lái)說(shuō)說(shuō)幾種磁盤(pán)緩存的策略:
- DiskCacheStrategy.NONE:表示不緩存任何內(nèi)容
- DiskCacheStrategy.SOURCE:表示只緩存原始圖片
- DiskCacheStrategy.RESULT:表示只緩存轉(zhuǎn)換后的圖片(默認(rèn)選項(xiàng))
- DiskCacheStrategy.ALL:表示既緩存原始圖片怒坯,也緩存轉(zhuǎn)換后的圖片
注:Glide 加載一張圖時(shí)竖席,默認(rèn)不會(huì)展示原圖,而是會(huì)對(duì)圖片進(jìn)行壓縮和轉(zhuǎn)換敬肚,比如對(duì)圖片進(jìn)行尺寸壓縮到 目標(biāo)View大小。
磁盤(pán)緩存使用的也是 LRU 算法束析,對(duì)應(yīng)的實(shí)現(xiàn)工具為 DiskLruCache 艳馒,整個(gè)處理流程接著上面內(nèi)存緩存的流程開(kāi)始,也即是 新開(kāi) EngineRunnable 任務(wù)加載圖片员寇,這一步會(huì)先判斷是否進(jìn)行過(guò)磁盤(pán)緩存弄慰,若可以則直接從磁盤(pán)緩存取,否則重新下載蝶锋。流程如下:關(guān)于 Glide 的緩存機(jī)制目前就到這里陆爽,簡(jiǎn)單總結(jié)就是:
- 若允許內(nèi)存緩存,先從內(nèi)存LruCache中查找扳缕,若存在則將該資源添加到 activeResouces 弱引用map中然后返回慌闭,該 map 主要用來(lái)緩存當(dāng)前正在使用的資源,若不存在則判斷 activeResouces 弱引用map 中是否存在躯舔,若存在則返回驴剔,每次資源處于正在使用狀態(tài),則引用計(jì)數(shù)加一粥庄,若未使用丧失,則緩存到 LruCache 中;
- 若內(nèi)存中不存在惜互,若允許磁盤(pán)緩存布讹,再?gòu)拇疟P(pán)緩存中查找琳拭;
- 若磁盤(pán)中不存在,則直接下載描验,然后再根據(jù)磁盤(pán)緩存策略緩存原圖或是經(jīng)轉(zhuǎn)換后的圖到磁盤(pán)中白嘁。
總結(jié)
Glide 的功能之強(qiáng)大,還有很多沒(méi)整理到挠乳,更多來(lái)了解和源碼分析权薯,鏈接:https://blog.csdn.net/guolin_blog/article/details/53759439