相關文章
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() 到底做了什么猪瞬?
==關鍵類==:RequestManagerRetriever陈瘦、RequestManager
首先需要注意 with方法傳入的context對象將會決定我們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睦刃。其實都是先調用RequestManagerRetriever的靜態(tài)get()方法得到一個RequestManagerRetriever對象枣宫,這個靜態(tài)get()方法是一個最基礎的單例模式也颤。然后再調用RequestManagerRetriever的實例get()方法翅娶,去獲取RequestManager對象竭沫。其實無非就是兩種情況而已蜕提,即傳入Application類型的參數谎势,和傳入非Application類型的參數脏榆。
傳入Application類型的參數
代碼如下:
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就是通過該類獲得坞生,就一個方法:getDescendants是己,作用就是基于給定的Context,獲取所有層級相關的RequestManager升熊。上下文層級由Activity或者Fragment獲得级野,ApplicationContext的上下文不會提供RequestManager的層級關系辰企,而且Application生命周期過長牢贸,所以Glide中對請求的控制只針對于Activity和Fragment。
繼續(xù)說竹习,傳入Application類型整陌,其實這是最簡單的一種情況泌辫,因為Application對象的生命周期即應用程序的生命周期甥郑,因此Glide并不需要做什么特殊的處理伍俘,它自動就是和應用程序的生命周期是同步的邪锌,如果應用程序關閉的話,Glide的加載也會同時終止癌瘾。
傳入非Application參數的情況
代碼如下:
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);
}
}
如果我們是在非主線程當中使用的Glide觅丰,那么不管你是傳入的Activity還是Fragment,都會被強制當成Application來處理妨退。
方法中傳入的是Activity妇萄、FragmentActivity、v4包下的Fragment咬荷、還是app包下的Fragment冠句,最終的流程都是一樣的聚唐,那就是會調用fragmentGet()方法,向當前的Activity當中添加一個隱藏的RequestManagerFragment。
其實,最終都是調用了fragmentGet()這個方法去獲取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());
//非常重要的一個方法,就是通過這個方法將我們空的fragment關聯(lián)到了Requestmanager關聯(lián)綁定到了一起
//RequestManager和RequestManagerFragment都是一一對應的
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其實就是一個RequestManager的生產類同欠。
那這個RequsetManager是干什么的呢忱反?
其實RequestManager是用于管理Glide的圖片加載請求的和完成glide對象的構造。最重要的一點就是用于監(jiān)聽我們整個組件的生命周期。
那么這里為什么要添加一個隱藏的Fragment呢?
因為Glide需要知道加載的生命周期。很簡單的一個道理,如果你在某個Activity上正在加載著一張圖片,結果圖片還沒加載出來,Activity就被用戶關掉了,那么圖片還應該繼續(xù)加載嗎?當然不應該。可是Glide并沒有辦法知道Activity的生命周期,于是Glide就使用了添加隱藏Fragment的這種小技巧,因為Fragment的生命周期和Activity是同步的低滩,如果Activity被銷毀了婶溯,F(xiàn)ragment是可以監(jiān)聽到的渔扎,這樣Glide就可以捕獲這個事件并停止圖片加載了倘核。
Glide精妙設計之一
with()的源碼設計中比較重要惠况,核心的一點來說就是榨了,將Glide和組件的生命周期相掛鉤唆垃。
總體來說矾瑰,第一個with()方法的源碼還是比較好理解的。其實就是為了得到一個RequestManager對象而已尊残,然后Glide會根據我們傳入with()方法的參數來確定圖片加載的生命周期扎阶,并沒有什么特別復雜的邏輯,就是一個準備好基礎配置的方法。
load()方法到底做了什么?
==關鍵詞==:
DrawableTypeRequest,GenericRequestBuilder(是我們在glide當中配置所有參數的父類枝冀,也就是說,只要是在glide當中配置參數鸵钝,就一定是通過這個類或者他的子類來實現(xiàn)的)
ModelLoader(通過數據來源,將數據來源加載成原始數據)
RequestTracker(直譯的話就是請求追蹤器申屹,跟蹤圖片請求的整個周期决乎,可以做取消丑蛤,重啟一些失敗的圖片請求生命周期的管理主要由RequestTracker和TargetTracker處理砰盐。builder.createGlide() 創(chuàng)建Glide對象列疗。
由于with()方法返回的是一個RequestManager對象疤剑,那么很容易就能想到吨悍,load()方法是在RequestManager類當中的,所以說我們首先要看的就是RequestManager這個類。
那么我們先來看load()方法菱蔬,這個方法中的邏輯是非常簡單的掉房,只有一行代碼讨阻,就是先調用了fromString()方法醇坝,再調用load()方法摊腋,然后把傳入的圖片URL地址傳進去。(也可以從源碼中看出础锐,load有多個重載方法耀怜,支持String,file,Integer,byte等各種數據來源)
而fromString()方法也極為簡單,就是調用了loadGeneric()方法,并且指定參數為String.class,因為load()方法傳入的是一個字符串參數恩够。那么看上去疆股,好像主要的工作都是在loadGeneric()方法中進行的了峰鄙。
/**
* 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)點進去可以看到他不僅返回了ModelLoader對象浸间,而且還初始化了Glide。點進去看看:
/**
* 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標簽吟榴,返回一個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集合之后吩翻,遍歷了集合并調用相應的applyOptions和registerComponents方法兜看,而Glide對象的生成是通過GlideBuilder的createGlide方法創(chuàng)建。(底下有例子)
看到這里不知道大家會不會跟我有一樣的疑問狭瞎,就是
GlideModule是個啥细移?干嘛用的?
可以通過GlideBuilder進行一些延遲的配置和ModelLoaders的注冊熊锭。注意:
所有的實現(xiàn)的module必須是public的弧轧,并且只擁有一個空的構造函數,以便Glide懶加載的時候可以通過反射調用球涛。
GlideModule是不能指定調用順序的劣针。因此在創(chuàng)建多個GlideModule的時候,要注意不同Module之間的setting不要沖突了亿扁。
接下來看一下glide = builder.createGlide();這句代碼做了什么
Glide createGlide() {
if (sourceService == null) {
//查看核心線程數
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();
}
}
////內存緩存用的是targetMemoryCacheSize (即一般是緩存大小是屏幕的寬 * 高 * 4 * 2)
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
//磁盤緩存 默認大小:250 MB,默認目錄:image_manager_disk_cache.
//Glide默認是用InternalCacheDiskCacheFactory類來創(chuàng)建硬盤緩存的捺典,這個類會在應用的內部緩存目錄下面創(chuàng)建一個最大容量250MB的緩存文件夾,使用這個緩存目錄而不用sd卡从祝,意味著除了本應用之外襟己,其他應用是不能訪問緩存的圖片文件的。
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);
}
看到這里其實大體的glide所做的內容我們已經清楚牍陌,其實Glide還支持動態(tài)的緩存大小調整擎浴,在存在大量圖片的Activity/Fragment中,可以通過setMemoryCategory方法來提高Glide的內存緩存大小毒涧,從而加快圖片的加載速度贮预。
Glide.get(getApplicationContext()).setMemoryCategory(MemoryCategory.HIGH);
MemoryCategory有3個值可供選擇:
- MemoryCategory.HIGH(初始緩存大小的1.5倍)
- MemoryCategory.NORMAL(初始緩存大小的1倍)
- MemoryCategory.LOW(初始緩存大小的0.5倍)
Glide磁盤緩存策略分為四種,默認的是RESULT:
- ALL:緩存原圖和處理圖
- NONE:什么都不緩存
- SOURCE:只緩存原圖
- RESULT:只緩存處理圖
這么惡心的ModelLoader到底是干嘛用的?
ModelLoader對象是用于加載圖片各種資源的,而我們給load()方法傳入不同類型的參數仿吞,這里也會得到不同的ModelLoader對象滑频。該接口有兩個目的:將任意復雜的model轉換為可以被decode的數據類型,允許model結合View的尺寸獲取特定大小的資源
最后我們可以看到唤冈,loadGeneric()方法是要返回一個DrawableTypeRequest對象的峡迷,因此在loadGeneric()方法的最后又去new了一個DrawableTypeRequest對象,然后把剛才獲得的ModelLoader對象你虹,還有一大堆雜七雜八的東西都傳了進去绘搞。
那DrawableTypeRequest是做什么的呢
DrawableTypeRequest
這個類中的代碼本身就不多,主要看一下構造方法和我們會用到的兩個比較重要的方法asGif()和asBitmap()傅物。這兩個方法分別是用于強制指定加載靜態(tài)圖片和動態(tài)圖片夯辖。將我們的圖片轉化為BitmapTypeRequest或者GifTypeRequest兩種圖片格式。
asBitmap()與asGif()
不管我們傳入的是一張普通圖片董饰,還是一張GIF圖片楼雹,Glide都會自動進行判斷,并且可以正確地把它解析并展示出來尖阔。
但是如果我想指定圖片的格式該怎么辦呢?就比如說榨咐,我希望加載的這張圖必須是一張靜態(tài)圖片介却,我不需要Glide自動幫我判斷它到底是靜圖還是GIF圖。
好的我們只需要反向操作下块茁,兩種情況:
1. 傳入gif鏈接齿坷,使用asBitmap()方法,gif圖則無法正常播放数焊,而是會停在第一幀的圖片永淌。
2. 傳入靜態(tài)圖片鏈接,使用asGif()方法佩耳,會顯示error()設置的圖片遂蛀,沒錯,如果指定了只能加載動態(tài)圖片干厚,而傳入的圖片卻是一張靜圖的話李滴,那么結果自然就只有加載失敗。
看一下代碼:
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)建了一個BitmapTypeRequest和GifTypeRequest所坯,如果沒有進行強制指定的話,那默認就是使用DrawableTypeRequest挂捅。
好的芹助,那么我們再回到RequestManager的load()方法中。剛才已經分析過了,fromString()方法會返回一個DrawableTypeRequest對象状土,接下來會調用這個對象的load()方法无蜂,把圖片的URL地址傳進去。點進去看看load()是在DrawableRequestBuilder類中声诸,我們也可以看到DrawableRequestBuilder是DrawableTypeRequest的父類酱讶。看代碼:
@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; //注意這個boolean 在into方法時我們會講到
return this;
}
其實這個model是什么彼乌,說白了就是我們傳進來的數據對象泻肯。就是數據來源,可以支持多種類型慰照,圖片灶挟,url,字節(jié)毒租,文件等稚铣。
DrawableTypeRequest的父類是DrawableRequestBuilder,DrawableRequestBuilder中有很多個方法墅垮,這些方法其實就是Glide絕大多數的API了惕医。里面有不少我們在上篇文章中已經用過了,比如說placeholder()方法算色、error()方法抬伺、diskCacheStrategy()方法、override()方法灾梦,當然還有最重要的into()方法峡钓。其實通過源碼得知,DrawableRequestBuilder在這些方法中也沒有做什么處理若河,主要是通過父類的方法來做相應處理能岩。
最重要的來了,在DrawableRequestBuilder類中有一個into()方法萧福,也就是說拉鹃,最終load()方法返回的其實就是一個DrawableTypeRequest對象。
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
Glide精妙設計之二
其實通過Glide支持鏈式調用就可以知道鲫忍,他是使用了建造者模式構建的毛俏,類似于我們的Dialog,Retrofit饲窿。泛型煌寇,接口的使用。