相關(guān)文章
Glide源碼分析之一
Glide源碼分析之二
Glide源碼分析之三
文章基于3.7.0。主要參考郭神的Glide源碼解析。
簡單使用
String imgUrl = "https://www.baidu.com/img/bd_logo1.png?where=super";
Glide.with(this).load(imgUrl).into(imageView);
Glide.with(getApplicationContext())
.load(imgUrl)
.asGif()
.asBitmap()
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.override(300,300)
.fitCenter()
.centerCrop()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.priority(Priority.HIGH)
.into(imageView);
Model -(ModelLoader)-> Data -(Decoder)-> Resource -(Transform)-> TransformedResource -(Transcode)-> TranscodedResource --> Target
with() 到底做了什么?
==關(guān)鍵類==:RequestManagerRetriever、RequestManager
首先需要注意 with方法傳入的context對象將會(huì)決定我們Glide存活的生命周期。
/** Begin a load with Glide by passing in a context.
* <p>
* This method is appropriate for resources that will be used outside of the normal fragment or activity
* lifecycle (For example in services, or for notification thumbnails).
* </p>
*
* @see #with(android.app.Activity)
* @see #with(android.app.Fragment)
* @see #with(android.support.v4.app.Fragment)
* @see #with(android.support.v4.app.FragmentActivity)
*
* @param context Any context, will not be retained.
* @return A RequestManager for the top level application that can be used to start a load.
*/
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
/**
* Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle and that uses the
* given {@link Activity}'s default options.
*
* @param activity The activity to use.
* @return A RequestManager for the given activity that can be used to start a load.
*/
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
可以看到,with()方法的重載種類非常多,既可以傳入Activity,也可以傳入Fragment或者是Context。其實(shí)都是先調(diào)用RequestManagerRetriever的靜態(tài)get()方法得到一個(gè)RequestManagerRetriever對象,這個(gè)靜態(tài)get()方法是一個(gè)最基礎(chǔ)的單例模式。然后再調(diào)用RequestManagerRetriever的實(shí)例get()方法,去獲取RequestManager對象。其實(shí)無非就是兩種情況而已,即傳入Application類型的參數(shù),和傳入非Application類型的參數(shù)。
傳入Application類型的參數(shù)
代碼如下:
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
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);
}
private RequestManager getApplicationManager(Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
// However, in this case since the manager attached to the application will not receive lifecycle
// events, we must force the manager to start resumed using ApplicationLifecycle.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
這里插一句 不知道有沒有人好奇
RequestManagerTreeNode是干嘛的呢?
上文提到獲取所有childRequestManagerFragments的RequestManager就是通過該類獲得,就一個(gè)方法:getDescendants,作用就是基于給定的Context,獲取所有層級相關(guān)的RequestManager。上下文層級由Activity或者Fragment獲得,ApplicationContext的上下文不會(huì)提供RequestManager的層級關(guān)系,而且Application生命周期過長,所以Glide中對請求的控制只針對于Activity和Fragment。
繼續(xù)說,傳入Application類型,其實(shí)這是最簡單的一種情況,因?yàn)锳pplication對象的生命周期即應(yīng)用程序的生命周期,因此Glide并不需要做什么特殊的處理,它自動(dòng)就是和應(yīng)用程序的生命周期是同步的,如果應(yīng)用程序關(guān)閉的話,Glide的加載也會(huì)同時(shí)終止。
傳入非Application參數(shù)的情況
代碼如下:
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@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);
}
}
如果我們是在非主線程當(dāng)中使用的Glide,那么不管你是傳入的Activity還是Fragment,都會(huì)被強(qiáng)制當(dāng)成Application來處理。
方法中傳入的是Activity、FragmentActivity、v4包下的Fragment、還是app包下的Fragment,最終的流程都是一樣的,那就是會(huì)調(diào)用fragmentGet()方法,向當(dāng)前的Activity當(dāng)中添加一個(gè)隱藏的RequestManagerFragment。
其實(shí),最終都是調(diào)用了fragmentGet()這個(gè)方法去獲取RequestManager,
@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());
//非常重要的一個(gè)方法,就是通過這個(gè)方法將我們空的fragment關(guān)聯(lián)到了Requestmanager關(guān)聯(lián)綁定到了一起
//RequestManager和RequestManagerFragment都是一一對應(yīng)的
current.setRequestManager(requestManager);
}
return requestManager;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//這里
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
可以看到RequestManagerRetriever其實(shí)就是一個(gè)RequestManager的生產(chǎn)類。
那這個(gè)RequsetManager是干什么的呢?
其實(shí)RequestManager是用于管理Glide的圖片加載請求的和完成glide對象的構(gòu)造。最重要的一點(diǎn)就是用于監(jiān)聽我們整個(gè)組件的生命周期。
那么這里為什么要添加一個(gè)隱藏的Fragment呢?
因?yàn)镚lide需要知道加載的生命周期。很簡單的一個(gè)道理,如果你在某個(gè)Activity上正在加載著一張圖片,結(jié)果圖片還沒加載出來,Activity就被用戶關(guān)掉了,那么圖片還應(yīng)該繼續(xù)加載嗎?當(dāng)然不應(yīng)該??墒荊lide并沒有辦法知道Activity的生命周期,于是Glide就使用了添加隱藏Fragment的這種小技巧,因?yàn)镕ragment的生命周期和Activity是同步的,如果Activity被銷毀了,F(xiàn)ragment是可以監(jiān)聽到的,這樣Glide就可以捕獲這個(gè)事件并停止圖片加載了。

Glide精妙設(shè)計(jì)之一
with()的源碼設(shè)計(jì)中比較重要,核心的一點(diǎn)來說就是,將Glide和組件的生命周期相掛鉤。
總體來說,第一個(gè)with()方法的源碼還是比較好理解的。其實(shí)就是為了得到一個(gè)RequestManager對象而已,然后Glide會(huì)根據(jù)我們傳入with()方法的參數(shù)來確定圖片加載的生命周期,并沒有什么特別復(fù)雜的邏輯,就是一個(gè)準(zhǔn)備好基礎(chǔ)配置的方法。
load()方法到底做了什么?
==關(guān)鍵詞==:
DrawableTypeRequest,GenericRequestBuilder(是我們在glide當(dāng)中配置所有參數(shù)的父類,也就是說,只要是在glide當(dāng)中配置參數(shù),就一定是通過這個(gè)類或者他的子類來實(shí)現(xiàn)的)
ModelLoader(通過數(shù)據(jù)來源,將數(shù)據(jù)來源加載成原始數(shù)據(jù))
RequestTracker(直譯的話就是請求追蹤器,跟蹤圖片請求的整個(gè)周期,可以做取消,重啟一些失敗的圖片請求生命周期的管理主要由RequestTracker和TargetTracker處理。builder.createGlide() 創(chuàng)建Glide對象。
由于with()方法返回的是一個(gè)RequestManager對象,那么很容易就能想到,load()方法是在RequestManager類當(dāng)中的,所以說我們首先要看的就是RequestManager這個(gè)類。
那么我們先來看load()方法,這個(gè)方法中的邏輯是非常簡單的,只有一行代碼,就是先調(diào)用了fromString()方法,再調(diào)用load()方法,然后把傳入的圖片URL地址傳進(jìn)去。(也可以從源碼中看出,load有多個(gè)重載方法,支持String,file,Integer,byte等各種數(shù)據(jù)來源)
而fromString()方法也極為簡單,就是調(diào)用了loadGeneric()方法,并且指定參數(shù)為String.class,因?yàn)閘oad()方法傳入的是一個(gè)字符串參數(shù)。那么看上去,好像主要的工作都是在loadGeneric()方法中進(jìn)行的了。
/**
* Returns a request builder to load the given {@link java.lang.String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
//傳入的是String的class對象
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);as
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
在loadGeneric()方法第一行可以看到有一句Glide.buildStreamModelLoader(modelClass, context)點(diǎn)進(jìn)去可以看到他不僅返回了ModelLoader對象,而且還初始化了Glide。點(diǎn)進(jìn)去看看:
/**
* A method to build a {@link ModelLoader} for the given model that produces {@link InputStream}s using a registered
* factory.
*
* @see #buildModelLoader(Class, Class, android.content.Context)
*/
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
return buildModelLoader(modelClass, InputStream.class, context);
}
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
Context context) {
if (modelClass == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to load null model, setting placeholder only");
}
return null;
}
return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}
/**
* Get the singleton.
*
* @return the singleton
*/
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
//解析清單文件配置的自定義GlideModule的metadata標(biāo)簽,返回一個(gè)GlideModule集合
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
//初始化了glide的單例。
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
我們看到通過反射的方式獲取我們在清單文件中聲明的自定義的GlideModule對象。在獲取到GlideModule集合之后,遍歷了集合并調(diào)用相應(yīng)的applyOptions和registerComponents方法,而Glide對象的生成是通過GlideBuilder的createGlide方法創(chuàng)建。(底下有例子)
看到這里不知道大家會(huì)不會(huì)跟我有一樣的疑問,就是
GlideModule是個(gè)啥?干嘛用的?
可以通過GlideBuilder進(jìn)行一些延遲的配置和ModelLoaders的注冊。注意:
所有的實(shí)現(xiàn)的module必須是public的,并且只擁有一個(gè)空的構(gòu)造函數(shù),以便Glide懶加載的時(shí)候可以通過反射調(diào)用。
GlideModule是不能指定調(diào)用順序的。因此在創(chuàng)建多個(gè)GlideModule的時(shí)候,要注意不同Module之間的setting不要沖突了。
接下來看一下glide = builder.createGlide();這句代碼做了什么
Glide createGlide() {
if (sourceService == null) {
//查看核心線程數(shù)
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
//初始化線程池
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
//初始化bitmapPool
//圖片池用的是targetPoolSize(即一般是緩存大小是屏幕的寬高4*4).
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
////內(nèi)存緩存用的是targetMemoryCacheSize (即一般是緩存大小是屏幕的寬 * 高 * 4 * 2)
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
//磁盤緩存 默認(rèn)大小:250 MB,默認(rèn)目錄:image_manager_disk_cache.
//Glide默認(rèn)是用InternalCacheDiskCacheFactory類來創(chuàng)建硬盤緩存的,這個(gè)類會(huì)在應(yīng)用的內(nèi)部緩存目錄下面創(chuàng)建一個(gè)最大容量250MB的緩存文件夾,使用這個(gè)緩存目錄而不用sd卡,意味著除了本應(yīng)用之外,其他應(yīng)用是不能訪問緩存的圖片文件的。
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
//引擎初始化
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
看到這里其實(shí)大體的glide所做的內(nèi)容我們已經(jīng)清楚,其實(shí)Glide還支持動(dòng)態(tài)的緩存大小調(diào)整,在存在大量圖片的Activity/Fragment中,可以通過setMemoryCategory方法來提高Glide的內(nèi)存緩存大小,從而加快圖片的加載速度。
Glide.get(getApplicationContext()).setMemoryCategory(MemoryCategory.HIGH);
MemoryCategory有3個(gè)值可供選擇:
- MemoryCategory.HIGH(初始緩存大小的1.5倍)
- MemoryCategory.NORMAL(初始緩存大小的1倍)
- MemoryCategory.LOW(初始緩存大小的0.5倍)
Glide磁盤緩存策略分為四種,默認(rèn)的是RESULT:
- ALL:緩存原圖和處理圖
- NONE:什么都不緩存
- SOURCE:只緩存原圖
- RESULT:只緩存處理圖
這么惡心的ModelLoader到底是干嘛用的?
ModelLoader對象是用于加載圖片各種資源的,而我們給load()方法傳入不同類型的參數(shù),這里也會(huì)得到不同的ModelLoader對象。該接口有兩個(gè)目的:將任意復(fù)雜的model轉(zhuǎn)換為可以被decode的數(shù)據(jù)類型,允許model結(jié)合View的尺寸獲取特定大小的資源
最后我們可以看到,loadGeneric()方法是要返回一個(gè)DrawableTypeRequest對象的,因此在loadGeneric()方法的最后又去new了一個(gè)DrawableTypeRequest對象,然后把剛才獲得的ModelLoader對象,還有一大堆雜七雜八的東西都傳了進(jìn)去。
那DrawableTypeRequest是做什么的呢
DrawableTypeRequest
這個(gè)類中的代碼本身就不多,主要看一下構(gòu)造方法和我們會(huì)用到的兩個(gè)比較重要的方法asGif()和asBitmap()。這兩個(gè)方法分別是用于強(qiáng)制指定加載靜態(tài)圖片和動(dòng)態(tài)圖片。將我們的圖片轉(zhuǎn)化為BitmapTypeRequest或者GifTypeRequest兩種圖片格式。
asBitmap()與asGif()
不管我們傳入的是一張普通圖片,還是一張GIF圖片,Glide都會(huì)自動(dòng)進(jìn)行判斷,并且可以正確地把它解析并展示出來。
但是如果我想指定圖片的格式該怎么辦呢?就比如說,我希望加載的這張圖必須是一張靜態(tài)圖片,我不需要Glide自動(dòng)幫我判斷它到底是靜圖還是GIF圖。
好的我們只需要反向操作下,兩種情況:
1. 傳入gif鏈接,使用asBitmap()方法,gif圖則無法正常播放,而是會(huì)停在第一幀的圖片。
2. 傳入靜態(tài)圖片鏈接,使用asGif()方法,會(huì)顯示error()設(shè)置的圖片,沒錯(cuò),如果指定了只能加載動(dòng)態(tài)圖片,而傳入的圖片卻是一張靜圖的話,那么結(jié)果自然就只有加載失敗。
看一下代碼:
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}
而從源碼中可以看出,它們分別又創(chuàng)建了一個(gè)BitmapTypeRequest和GifTypeRequest,如果沒有進(jìn)行強(qiáng)制指定的話,那默認(rèn)就是使用DrawableTypeRequest。
好的,那么我們再回到RequestManager的load()方法中。剛才已經(jīng)分析過了,fromString()方法會(huì)返回一個(gè)DrawableTypeRequest對象,接下來會(huì)調(diào)用這個(gè)對象的load()方法,把圖片的URL地址傳進(jìn)去。點(diǎn)進(jìn)去看看load()是在DrawableRequestBuilder類中,我們也可以看到DrawableRequestBuilder是DrawableTypeRequest的父類??创a:
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true; //注意這個(gè)boolean 在into方法時(shí)我們會(huì)講到
return this;
}
其實(shí)這個(gè)model是什么,說白了就是我們傳進(jìn)來的數(shù)據(jù)對象。就是數(shù)據(jù)來源,可以支持多種類型,圖片,url,字節(jié),文件等。
DrawableTypeRequest的父類是DrawableRequestBuilder,DrawableRequestBuilder中有很多個(gè)方法,這些方法其實(shí)就是Glide絕大多數(shù)的API了。里面有不少我們在上篇文章中已經(jīng)用過了,比如說placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法,當(dāng)然還有最重要的into()方法。其實(shí)通過源碼得知,DrawableRequestBuilder在這些方法中也沒有做什么處理,主要是通過父類的方法來做相應(yīng)處理。
最重要的來了,在DrawableRequestBuilder類中有一個(gè)into()方法,也就是說,最終load()方法返回的其實(shí)就是一個(gè)DrawableTypeRequest對象。
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
Glide精妙設(shè)計(jì)之二
其實(shí)通過Glide支持鏈?zhǔn)秸{(diào)用就可以知道,他是使用了建造者模式構(gòu)建的,類似于我們的Dialog,Retrofit。泛型,接口的使用。