從源碼分析Android的Glide庫(kù)的圖片加載流程及特點(diǎn)
0.基礎(chǔ)知識(shí)
Glide中有一部分單詞,我不知道用什么中文可以確切的表達(dá)出含義,用英文單詞可能在行文中更加合適,還有一些詞在Glide中有特別的含義,我理解的可能也不深入,這里先記錄一下。
(1)View: 一般情況下,指Android中的View及其子類(lèi)控件(包括自定義的),尤其指ImageView。這些控件可在上面繪制Drawable
(2)Target: Glide中重要的概念,目標(biāo)。它即可以指封裝了一個(gè)View的Target(ViewTarget),也可以不包含View(SimpleTarget)。
(3)Drawable: 指Android中的Drawable類(lèi)或者它的子類(lèi),如BitmapDrawable等?;蛘逩lide中基礎(chǔ)Drawable實(shí)現(xiàn)的自定義Drawable(如GifDrawable等)
(4)Request - 加載請(qǐng)求,可以是網(wǎng)絡(luò)請(qǐng)求或者其他任何下載圖片的請(qǐng)求,也是Glide中的一個(gè)類(lèi)。
(5)Model:數(shù)據(jù)源的提供者,如Url,文件路徑等,可以從model中獲取InputStream。
(6)Signature:簽名,可以唯一地標(biāo)識(shí)一個(gè)對(duì)象。
(7)recycle():Glide中Resource類(lèi)有此方法,表示該資源不被引用,可以放入池中(此時(shí)并沒(méi)有釋放空間)。Android中Bitmap也有此方法,表示釋放Bitmap占用的內(nèi)存。
1.主要特點(diǎn)
(1)支持Memory和Disk圖片緩存。
(2)支持gif和webp格式圖片。
(3)根據(jù)Activity/Fragment生命周期自動(dòng)管理請(qǐng)求。
(4)使用Bitmap Pool可以使Bitmap復(fù)用。
(5)對(duì)于回收的Bitmap會(huì)主動(dòng)調(diào)用recycle,減小系統(tǒng)回收壓力。
2. 總體設(shè)計(jì)

基本概念
RequestManager:請(qǐng)求管理,每一個(gè)Activity都會(huì)創(chuàng)建一個(gè)RequestManager,根據(jù)對(duì)應(yīng)Activity的生命周期管理該Activity上所以的圖片請(qǐng)求。
Engine:加載圖片的引擎,根據(jù)Request創(chuàng)建EngineJob和DecodeJob。
EngineJob:圖片加載。
DecodeJob:圖片處理。
流程圖
這里是大概的總體流程圖, 具體的細(xì)節(jié)中流程下面繼續(xù)分析。

3. 核心類(lèi)介紹
3.1 Gilde
用于保存整個(gè)框架中的配置。
重要方法:
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
用于創(chuàng)建RequestManager,這里是Glide通過(guò)Activity/Fragment生命周期管理Request原理所在,這個(gè)類(lèi)很關(guān)鍵、很關(guān)鍵、很關(guān)鍵,重要的事情我只說(shuō)三遍。
主要原理是創(chuàng)建一個(gè)自定義Fragment,然后通過(guò)自定義Fragment生命周期操作RequestManager,從而達(dá)到管理Request。

3.2 RequestManagerRetriever
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
這里判斷是否只當(dāng)前RequestManagerFragment是否存在RequestManager,保證一個(gè)Activity對(duì)應(yīng)一個(gè)RequestManager, 這樣有利于管理一個(gè)Activity上所有的Request。創(chuàng)建RequestManager的時(shí)候會(huì)將RequestManagerFragment中的回調(diào)接口賦值給RequestManager,達(dá)到RequestManager監(jiān)聽(tīng)RequestManagerFragment的生命周期。
3.3 RequestManager
成員變量:
(1)Lifecycle lifecycle,用于監(jiān)聽(tīng)RequestManagerFragment生命周期。
(2)RequestTracker requestTracker, 用于保存當(dāng)前RequestManager所有的請(qǐng)求和帶處理的請(qǐng)求。
重要方法:
@Override
//開(kāi)始暫停的請(qǐng)求
public void onStart() {
resumeRequests();
}
//停止所有的請(qǐng)求
@Override
public void onStop() {
pauseRequests();
}
//關(guān)閉所以的請(qǐng)求
@Override
public void onDestroy() {
requestTracker.clearRequests();
}
//創(chuàng)建RequestBuild
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public <Y extends Target<TranscodeType>> Y into(Y target) {
...
Request previous = target.getRequest();
//停止當(dāng)前target中的Request。
if (previous != null) {
previous.clear(); //這個(gè)地方很關(guān)鍵,見(jiàn)Request解析
requestTracker.removeRequest(previous);
previous.recycle();
}
...
return target;
}
3.4 DrawableRequestBuilder
用于創(chuàng)建Request。 這里面包括很多方法,主要是配置加載圖片的url、大小、動(dòng)畫(huà)、ImageView對(duì)象、自定義圖片處理接口等。
3.5 Request
主要是操作請(qǐng)求,方法都很簡(jiǎn)單。
@Override
public void clear() {
...
if (resource != null) {
//這里會(huì)釋放資源
releaseResource(resource);
}
...
}
這里的基本原理是當(dāng)有Target使用Resource(Resource見(jiàn)下文)時(shí),Resource中的引用記數(shù)值會(huì)加一,當(dāng)釋放資源Resource中的引用記數(shù)值減一。當(dāng)沒(méi)有Target使用的時(shí)候就會(huì)釋放資源,放進(jìn)Lrucache中。
3.6 EngineResource
實(shí)現(xiàn)Resource接口,使用裝飾模式,里面包含實(shí)際的Resource對(duì)象
void release() {
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
void acquire() {
++acquired;
}
@Override
public void recycle() {
isRecycled = true;
resource.recycle();
}
acquire和release兩個(gè)方法是對(duì)資源引用計(jì)數(shù);recycle釋放資源,一般在Lrucache飽和時(shí)會(huì)觸發(fā)。
3.7 Engine(重要)
請(qǐng)求引擎,主要做請(qǐng)求的開(kāi)始的初始化。
3.7.1 load方法
這個(gè)方法很長(zhǎng),將分為幾步分析
(1)獲取MemoryCache中緩存 首先創(chuàng)建當(dāng)前Request的緩存key,通過(guò)key值從MemoryCache中獲取緩存,判斷緩存是否存在。
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
....
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
@SuppressWarnings("unchecked")
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource result;
...
return result;
}
(重點(diǎn))從緩存中獲取的時(shí)候使用的cache.remove(key),然后將值保存在activeResources中,然后將Resource的引用計(jì)數(shù)加一。
優(yōu)點(diǎn):
> 正使用的Resource將會(huì)在activeResources中,不會(huì)出現(xiàn)在cache中,當(dāng)MemoryCache中緩存飽和的時(shí)候或者系統(tǒng)內(nèi)存不足的時(shí)候,清理Bitmap可以直接調(diào)用recycle,不用考慮Bitmap正在使用導(dǎo)致異常,加快系統(tǒng)的回收。
(2)獲取activeResources中緩存
activeResources通過(guò)弱引用保存recouse ,也是通過(guò)key獲取緩存,
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable)
(3)判斷當(dāng)前的請(qǐng)求任務(wù)是否已經(jīng)存在
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
return new LoadStatus(cb, current);
}
如果任務(wù)請(qǐng)求已經(jīng)存在,直接將回調(diào)事件傳遞給已經(jīng)存在的EngineJob,用于請(qǐng)求成功后觸發(fā)回調(diào)。
(4)執(zhí)行請(qǐng)求任務(wù)
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(runnable);
3.8 EngineRunnable
請(qǐng)求執(zhí)行Runnable,主要功能請(qǐng)求資源、處理資源、緩存資源。
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
加載DiskCache和網(wǎng)絡(luò)資源。加載DiskCache包括兩個(gè),因?yàn)镚lide默認(rèn)是保存處理后的資源(壓縮和裁剪后),緩存方式可以自定義配置。如果客戶(hù)端規(guī)范設(shè)計(jì),ImageView大小大部分相同可以節(jié)省圖片加載時(shí)間和Disk資源。
3.9 DecodeJob
public Resource<Z> decodeResultFromCache() throws Exception
從緩存中獲取處理后的資源。上面有關(guān)Key的內(nèi)容,Key是一個(gè)對(duì)象,可以獲取key和orginKey。decodeResultFromCache就是通過(guò)key獲取緩存,decodeSourceFromCache()就是通過(guò)orginKey獲取緩存。
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded)
處理和包裝資源;緩存資源。
保存原資源
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException
保存處理后的資源
private void writeTransformedToCache(Resource<T> transformed)
3.10 Transformation
Resource<T> transform(Resource<T> resource, int outWidth, int outHeight);
處理資源,這里面出現(xiàn)BitmapPool類(lèi),達(dá)到Bitmap復(fù)用。
3.11 ResourceDecoder
用于將文件、IO流轉(zhuǎn)化為Resource
3.12 BitmapPool
用于存放從LruCache中remove的Bitmap, 用于后面創(chuàng)建Bitmap時(shí)候的重復(fù)利用。
4.雜談
Glide的架構(gòu)擴(kuò)展性高,但是難以理解,各種接口、泛型,需要一定的學(xué)習(xí)才能熟練運(yùn)用。
Glide的優(yōu)點(diǎn):
(1)支持對(duì)處理后的資源Disk緩存。
(2)通過(guò)BitmapPool對(duì)Bitmap復(fù)用。
(3)使用activityResources緩存正在使用的resource,對(duì)于BitmapPool飽和移除的Bitmap直接調(diào)用recycle加速內(nèi)存回收。
相關(guān)文章
Android開(kāi)發(fā)中requestfocus()無(wú)效的原因及解決辦法
這篇文章主要介紹了Android開(kāi)發(fā)中requestfocus()無(wú)效的原因及解決辦法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08
Android VideoView類(lèi)實(shí)例講解
本文主要介紹Android VideoView類(lèi),這里對(duì)VideoView類(lèi)詳細(xì)說(shuō)明了使用方法,以及示例代碼,有興趣的朋友可以參考下,希望能幫助Android 開(kāi)發(fā)的朋友2016-08-08
基于linux與windows平臺(tái)下 如何下載android sdk源代碼的方法詳解
本文主要是介紹在linux和windows平臺(tái)下,如何下載android sdk的源代碼,注意是sdk的源代碼,而不是android的所有源代碼,同時(shí)介紹如何把sdk源代碼加入到eclipse里,使android 平臺(tái)手機(jī)開(kāi)發(fā)者可以直接查看源代碼,通過(guò)閱讀SDK源碼,能更好的理解和運(yùn)用Android的API2013-05-05
android 跳轉(zhuǎn)進(jìn)市場(chǎng)的實(shí)現(xiàn)代碼
本篇文章是對(duì)android中跳轉(zhuǎn)進(jìn)市場(chǎng)的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
詳解 Kotlin Reference Basic Types, String, Array and Imports
這篇文章主要介紹了詳解 Kotlin Reference Basic Types, String, Array and Imports的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android中TelephonyManager用法實(shí)例
這篇文章主要介紹了Android中TelephonyManager用法,結(jié)合實(shí)例形式分析了TelephonyManager類(lèi)的功能,使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-03-03
android編程實(shí)現(xiàn)局部界面動(dòng)態(tài)切換的方法
這篇文章主要介紹了android編程實(shí)現(xiàn)局部界面動(dòng)態(tài)切換的方法,以實(shí)例形式較為詳細(xì)的分析了Android局部切換的布局及功能實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
Android開(kāi)發(fā)中Eclipse報(bào)錯(cuò)及對(duì)應(yīng)處理方法總結(jié)
這篇文章主要介紹了Android開(kāi)發(fā)中Eclipse報(bào)錯(cuò)及對(duì)應(yīng)處理方法,實(shí)例匯總了使用eclipse開(kāi)發(fā)Android項(xiàng)目過(guò)程中常見(jiàn)的錯(cuò)誤提示及對(duì)應(yīng)的處理技巧,需要的朋友可以參考下2015-12-12

