Glide的簡介就不多說了,只要記住這是Google推薦AndroidDev使用的圖片開源框架就好了秤掌。直接進(jìn)入正題愁铺,看看Glide的設(shè)計到底有何精妙之處,為何獲得了Google的青睞闻鉴?
一個最簡單的Glide圖片加載邏輯舉例:
Glide.with(context).load(url).into(imageView);
我們的分析會以Glide3.8.0版本為例茵乱,基于此行代碼展開,分別分析with()孟岛、load()瓶竭、into()三個方法內(nèi)部的邏輯督勺。
一、基礎(chǔ)概念
Glide中涉及幾個概念斤贰,為便于理解智哀,先粗略解釋:
1.Model
指的是對圖片源的封裝。在AndroidDev中荧恍,比較常見的就是網(wǎng)絡(luò)圖片地址瓷叫、本地文件或資源ID。比如块饺,String url = "https://www.baidu.com/img/bd_logo1.png"赞辩,這里的url經(jīng)過Glide內(nèi)部的封裝后,就可以理解為一個Model授艰。
2.Data
指的是對Model處理后的數(shù)據(jù)源的封裝辨嗽,通常是InputStream。而在Glide中淮腾,將Model處理為Data的類便是ModelLoader糟需。比如,通過上文的"https://www.baidu.com/img/bd_logo1.png"而取得的圖片輸入流谷朝,就可以理解為Data洲押。
3.Resource
拿到圖片的輸入流可以直接展示在UI上嗎?顯然不可以圆凰。在第2點中我們提及了Data杈帐,即輸入流,如果要將其展示就需要對Data進(jìn)行解碼专钉,解碼后的數(shù)據(jù)就是Resource挑童。比如,上文的圖片輸入流經(jīng)過解碼后成為一個Bitmap對象或Drawable對象跃须,這個對象就可以理解為Resource站叼。而擔(dān)任解碼任務(wù)的角色,被成為ResourceDecoder(資源解碼器)菇民。
4.TransformedResource&TranscodedResource
有時候我們獲取到的Resource并不適合展示尽楔,而是需要經(jīng)過處理才能展示,比如需要裁剪變換等(如調(diào)用centerCrop()第练、fitCenter())阔馋,那么經(jīng)過如此變換后的Rescourse稱為TransformedResource。
我們知道Glide是可以展示靜態(tài)圖娇掏,也可以展示動態(tài)圖(動態(tài)Gif)垦缅,而解碼后的靜態(tài)圖片和動態(tài)圖片(例如drawable和gif-drawable)類型是不同的, 為了統(tǒng)一處理邏輯驹碍,Glide內(nèi)部將這兩種類型的對象再次封裝為統(tǒng)一的GlideBitmapDrawable壁涎,這里的GlideBitmapDrawable就可以理解為TranscodedResource凡恍。
5.Target
這個比較容易理解,即需要在哪個目標(biāo)上進(jìn)行展示怔球,比如ImageView嚼酝。而Glide內(nèi)部將ImageView再次進(jìn)行了封裝,封裝后對象就可以理解為Target竟坛。
綜上所述闽巩,一個完整的圖片處理及展示流程如下:
二、基本用法
直接看代碼和注釋:
Glide.with(context)
.load(url)
.placeholder(R.mipmap.ic_launcher)//圖片加載前的占位圖
.error(R.mipmap.ic_launcher)//圖片加載錯誤的占位圖
.fitCenter()
.centerCrop()
.override(500, 500)//調(diào)整圖片大小
.skipMemoryCache(true)//跳過內(nèi)存緩存
.crossFade(1000)//漸變顯示時間
.diskCacheStrategy(DiskCacheStrategy.RESULT)//緩存處理后的圖像(如尺寸調(diào)整担汤、裁剪后的圖像)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)//緩存原尺寸的圖像
.diskCacheStrategy(DiskCacheStrategy.ALL)//緩存所有圖像
.diskCacheStrategy(DiskCacheStrategy.NONE)//跳過磁盤緩存
.priority(Priority.HIGH)//指定優(yōu)先級
.into(imageView);
三涎跨、with()源碼邏輯
跟進(jìn)Glide類中尋找with()方法,發(fā)現(xiàn)with()方法有多種重載崭歧,包括with(Context context)隅很、with(Activity activity)、with(Fragment fragment)等等率碾。Glide之所以設(shè)計如此之多的with()方法重載叔营,其目的在于將圖片的加載與傳入的Context組件或Fragment的生命周期相關(guān)聯(lián)。比如所宰,當(dāng)用戶關(guān)閉當(dāng)前Activity绒尊,那么即使該Activity有正在進(jìn)行中的圖片網(wǎng)絡(luò)請求,Glide也會隨之取消該網(wǎng)絡(luò)請求仔粥。如果傳入的是ApplicationContext婴谱,那么網(wǎng)絡(luò)請求會持續(xù)進(jìn)行,直至App進(jìn)程被銷毀躯泰。
這里以with(Context context)為例看一下源碼實現(xiàn)谭羔,其他重載方法邏輯基本一致:
Glide.java
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
RequestManager類是Glide中用來處理圖片加載請求的管理類。RequestManagerRetriever類是用來創(chuàng)建和管理RequestManager對象的斟冕,其get()方法獲取了RequestManagerRetriever實例。繼續(xù)追蹤源碼缅阳,查看RequestManagerRetriever類中g(shù)et()方法的邏輯磕蛇。
RequestManagerRetriever.java
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
在get()方法中,對Context的類型進(jìn)行了區(qū)分十办,有多種重載秀撇。我們主要分析一下Application和Activity類型的方法參數(shù),其他類型的參數(shù)讀者可以自行追蹤源碼向族。
RequestManagerRetriever.java
private RequestManager getApplicationManager(Context context) {
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
上述get(Application context)的方法中采取了雙重鎖單例模式獲取了一個RequestManager對象呵燕。可以發(fā)現(xiàn)件相,Glide在RequestManager的構(gòu)造方法中傳入了ApplicationLifecycle的實例再扭,其作用是將RequestManager與Application生命周期綁定氧苍。
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}
上述get(Activity context)的方法中,首先進(jìn)行了線程判斷泛范,如果在非主線程的情況下让虐,返回了get(Application context)類型的RequestManager,而在主線程的情況下罢荡,通過該Activity context獲取到了相關(guān)聯(lián)的FragmentManager赡突。這是Glide設(shè)計精妙之處之一,為什么要獲取FragmentManger呢区赵?繼續(xù)追蹤源碼到fragmentGet(activity惭缰,fm)中。
RequestManagerRetriever.java
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
分析一下fragmentGet()的邏輯笼才。首先漱受,方法第一行g(shù)etRequestManagerFragment()返回了RequestManagerFragment的實例current。RequestManagerFragment是啥患整?其實這是Glide封裝的Fragment拜效,這里當(dāng)作Fragment來理解。先來截取部分RequestManagerFragment類的代碼:
RequestManagerFragment.java
public class RequestManagerFragment extends Fragment {
......
private final ActivityFragmentLifecycle lifecycle;
private RequestManager requestManager;
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
/**
* Sets the current {@link com.bumptech.glide.RequestManager}.
*
* @param requestManager The request manager to use.
*/
public void setRequestManager(RequestManager requestManager) {
this.requestManager = requestManager;
}
ActivityFragmentLifecycle getLifecycle() {
return lifecycle;
}
/**
* Returns the current {@link com.bumptech.glide.RequestManager} or null if none exists.
*/
public RequestManager getRequestManager() {
return requestManager;
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
}
}
對RequestManagerFragment劃重點:
1.含有一個RequestManager類型的成員變量requestManager各谚,通過getRequestManager()返回紧憾,通過setRequestManager()賦值。
即每個RequestManagerFragment對象都與一個RequestManager對象綁定昌渤。
2.含有一個ActivityFragmentLifecycle類型的成員變量lifecycle赴穗。通過對lifecycle實現(xiàn)的LifecycleListener接口回調(diào)來監(jiān)聽其生命周期,可以發(fā)現(xiàn)在RequestManagerFragment類的onStart()膀息、onStop()般眉、onDestroy()方法中對其進(jìn)行了調(diào)用。
即lifecycle對象實現(xiàn)了對RequestManagerFragment實例的生命周期監(jiān)聽潜支。
回到fragmentGet()方法中甸赃,調(diào)用RequestManagerFragment中的getRequestManager()方法返回與其綁定的RequestManager對象(成員變量),如果為null(沒有與其綁定的RequestManager對象)冗酿,則創(chuàng)建一個RequestManager對象埠对,并調(diào)用setRequestManager()將其與RequestManagerFragment綁定。至此實現(xiàn)了RequestManagerFragment對象與RequestManager對象的一一綁定裁替。
現(xiàn)在思考一個問題项玛,Glide如何監(jiān)聽目標(biāo)Activity或Fragment的生命周期?上文已經(jīng)說明弱判,根據(jù)傳入Glide的Context不同襟沮,Glide會監(jiān)聽其生命周期,根據(jù)生命周期管理圖片的網(wǎng)絡(luò)請求。
Glide直接監(jiān)聽Activity并不方便开伏。那么在這里膀跌,Glide采用了另外的方法:
1.在Activity或Fragment的上層添再加一個Fragment,也就是上文一直在分析的RequestManagerFragment硅则。這個Fragment并沒有覆寫createFragmentView()淹父,即這是一個無UI的Fragment,當(dāng)添加了該Fragment怎虫,用戶是無法感知到的暑认。
2.我們知道依附于Activity的Fragment有與被依附的Activity相關(guān)聯(lián)的生命周期,監(jiān)聽Fragment的生命周期也就是獲取到了被依附的Activity的生命周期大审。上文已經(jīng)劃過重點:RequestManagerFragment通過接口回調(diào)的方式蘸际,在onStart()、onStop()徒扶、onDestroy()中調(diào)用了ActivityFragmentLifecycle的相應(yīng)生命周期方法粮彤,實現(xiàn)了對其生命周期的監(jiān)聽。
3.如果傳入Glide.with()參數(shù)為Fragment姜骡,那么處理邏輯同上导坟。大家應(yīng)該也在Fragment中添加過Fragment吧?Glide同樣監(jiān)聽了RequestManagerFragment的生命周期圈澈。
4.每個RequestManagerFragment對象又與RequestManager對象一一綁定惫周。至此,所有疑問解開: