前言
這篇圖害晦、文怒允、表惯疙、代碼一起組成的 Glide
源碼分析的文章是在上一篇文章 Android-Universal-Image-Loader源碼分析 中之后的又一篇圖片加載框架源碼解析簇抵,它也具備了 ImageLoader
中講述了Android
一個圖片加載庫所需要的一些基礎(chǔ)必備的:MemoryCahce
、DiskCahce
Decoder
DownLoader
和Executor
等部分门烂。這篇 Glide
的代碼分析量可以說至少是 ImageLoader
的3倍多宙枷,本來想對 Glide
代碼進行拆分钙畔,細(xì)化每個部分進行講解這個每個部分講的更加清楚一些甚疟。但最終還是打算整體一篇文章講完仗岖,因為我覺得整體性的學(xué)習(xí)能更深的的了解到 Glide
的框架的設(shè)計之美。
本篇文章講述的
Glide
相關(guān)知識比較多览妖,閱讀完需要大量的時間箩帚。所以我們按需分配,根據(jù)目錄尋找自己需要的知識點進行查看黄痪。
Glide介紹
Glide的Git地址:https://github.com/bumptech/glide
簡體中文文檔:https://muyangmin.github.io/glide-docs-cn/
Glide's documentation:https://bumptech.github.io/glide/
關(guān)于Glide
Glide
是一個快速高效的Android
圖片加載庫,注重于平滑的滾動盔然。Glide
提供了易用的API
桅打,高性能、可擴展的圖片解碼管道(decode pipeline
)愈案,以及自動的資源池技術(shù)挺尾。
Glide
支持拉取,解碼和展示視頻快照站绪,圖片遭铺,和GIF動畫。Glide
的Api是如此的靈活恢准,開發(fā)者甚至可以插入和替換成自己喜愛的任何網(wǎng)絡(luò)棧魂挂。默認(rèn)情況下,Glide
使用的是一個定制化的基于HttpUrlConnection
的棧馁筐,但同時也提供了與Google Volley
和Square OkHttp
快速集成的工具庫涂召。
雖然Glide
的主要目標(biāo)是讓任何形式的圖片列表的滾動盡可能地變得更快、更平滑敏沉,但實際上果正,Glide
幾乎能滿足你對遠(yuǎn)程圖片的拉取/縮放/顯示的一切需求炎码。
Glide性能
Glide
充分考慮了Android
圖片加載性能的兩個關(guān)鍵方面:
- 圖片解碼速度
- 解碼圖片帶來的資源壓力
為了讓用戶擁有良好的App使用體驗,圖片不僅要快速加載秋泳,而且還不能因為過多的主線程I/O或頻繁的垃圾回收導(dǎo)致頁面的閃爍和抖動現(xiàn)象潦闲。
Glide
使用了多個步驟來確保在Android
上加載圖片盡可能的快速和平滑:
- 自動、智能地下采樣(
downsampling
)和緩存(caching
)迫皱,以最小化存儲開銷和解碼次數(shù)歉闰; - 積極的資源重用,例如字節(jié)數(shù)組和
Bitmap
舍杜,以最小化昂貴的垃圾回收和堆碎片影響新娜; - 深度的生命周期集成,以確保僅優(yōu)先處理活躍的
Fragment
和Activity
的請求既绩,并有利于應(yīng)用在必要時釋放資源以避免在后臺時被殺掉概龄。
GlideAPI
Glide 使用簡明的流式語法API,這是一個非常棒的設(shè)計饲握,因為它允許你在大部分情況下一行代碼搞定需求:
Glide.with(fragment)
.load(url)
.into(imageView);
上述是Fragmeng
中Glide
將一張網(wǎng)絡(luò)圖片顯示到ImageView
的代碼私杜,下面源碼分析的時候我們也會用這段代碼進行分析,看看這么簡單的API
到底是怎么實現(xiàn)的救欧。
Glide源碼分析
我們學(xué)習(xí)和了解一些框架主要不是看它某個功能的具體實現(xiàn)衰粹,主要是學(xué)習(xí)框架結(jié)構(gòu)搭建和框架中模塊的設(shè)計與實現(xiàn)。當(dāng)然每個人的對每個框架的理解都各不相同笆怠,不過沒關(guān)系我們可以多學(xué)習(xí)多總結(jié)铝耻,慢慢培養(yǎng)我們自己的框架結(jié)構(gòu)意識。這個在我們平時開發(fā)過程中對我們幫助非常大蹬刷。
上圖是對我Glide
的一個總結(jié)^_^瓢捉。
Glide接入
Glide
的用法網(wǎng)上有很多文章講述的都非常好,這里不再進行講述办成。這塊主要想通過Glide
的配置來分析Glide
的運行機制泡态。
我們在使用Glide
的時候都會使用注解@GlideModule
實現(xiàn)AppGlideModule
或者 GeneratedAppGlideModule
,生成一個類名為GeneratedAppGlideModuleImpl
它是Glide
模塊的代理迂卢。 在GeneratedAppGlideModuleImpl
會包含我們自定義的GlideModel
某弦。
下面為我們接入項目的Glide
配置:
實現(xiàn)Glide對緩存的配置
@GlideModule
public final class GlideModuleConfig extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
super.applyOptions(context, builder);
long memoryCacheSizeBytes = 1024 * 1024 * 20;
builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
long diskCacheSizeBytes = 1024 * 1024 * 100;
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));
}
}
實現(xiàn)Okhttp的接入
@GlideModule
public final class OkHttpLibraryGlideModule extends LibraryGlideModule {
@Override
public void registerComponents(
@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
//將GlideUrl數(shù)據(jù)轉(zhuǎn)為InputStream的ModelLoader替換為Okhttp
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
我們編譯之后生成的GeneratedAppGlideModuleImpl
:
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final GlideModuleConfig appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new GlideModuleConfig();
}
//GlideBuilder的配置項進行應(yīng)用
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
//注冊自定義GlideModule
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
new OkHttpLibraryGlideModule().registerComponents(context, glide, registry);
appGlideModule.registerComponents(context, glide, registry);
}
/***部分代碼省略***/
}
下面將GeneratedAppGlideModuleImpl
的使用。
Glide初始化
public class Glide implements ComponentCallbacks2 {
/**
* Get the singleton.
* @return the singleton
*/
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
//
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
//通過Java反射機制獲取通過注解生成的GeneratedAppGlideModuleImpl
private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
GeneratedAppGlideModule result = null;
try {
Class<GeneratedAppGlideModule> clazz =
(Class<GeneratedAppGlideModule>)
Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
result =
clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
} catch (ClassNotFoundException e) {
/***部分代碼省略**/
}
return result;
}
}
Glide
是個單例而克,初始化的時候需要注解生成的GeneratedAppGlideModuleImpl
靶壮。
public class Glide implements ComponentCallbacks2 {
@GuardedBy("Glide.class")
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
@GuardedBy("Glide.class")
@SuppressWarnings("deprecation")
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
//如果Manifest文件中配置了GlideModule,并且annotationGeneratedModule允許Manifest文件中配置生效员萍,那么需要解析出來
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
//如果annotationGeneratedModule配置了擴展模塊需要解析出來亮钦,并且不能與果Manifest中的重復(fù)
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
/***省略部分日志***/
iterator.remove();
}
}
/***省略部分日志***/
//獲取Request的構(gòu)造管理工廠類
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
//Manifest中的配置項進行應(yīng)用
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
//annotationGeneratedModule進行配置項的應(yīng)用
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
//構(gòu)造Glide
Glide glide = builder.build(applicationContext);
//Manifest中的組件進行注冊
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
try {
module.registerComponents(applicationContext, glide, glide.registry);
} catch (AbstractMethodError e) {
/***省略部分日志***/
}
}
//annotationGeneratedModule中的組件進行注冊
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
//application注冊Glide組件
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
}
- 獲取
Manifest
的GlideModule
,反射構(gòu)造充活; - 獲取
GeneratedAppGlideModule
中擴展模塊蜂莉,如果包含Manifest
的擴展那么進行刪除蜡娶; - 獲取
RequestManagerFactroy
; -
Manifest.applyoptions
; -
GeneratedAppGlideModule.applytions
; - 構(gòu)建
Glide
; -
Manifest.registerComponents
; -
GeneratedAppGlideModule.registerComponents
;
Glide和Engine構(gòu)造
public class Glide implements ComponentCallbacks2 {
private final Engine engine;//負(fù)責(zé)啟動負(fù)載以及管理活動和緩存的資源。
private final BitmapPool bitmapPool;//Bitmap的緩存池(LruBitmapPool)
private final MemoryCache memoryCache;//Resource緩存池(LruResourceCache)
private final GlideContext glideContext;
private final Registry registry;
private final ArrayPool arrayPool;//存儲大小可變的數(shù)組的緩存池(LruArrayPool)
//一組靜態(tài)方法用來創(chuàng)建一個新的RequestManager或者從已經(jīng)存在的activity和fragment中獲取
private final RequestManagerRetriever requestManagerRetriever;
private final ConnectivityMonitorFactory connectivityMonitorFactory;
new Glide(
@NonNull context,//上下文環(huán)境
@NonNull engine,//任務(wù)執(zhí)行引擎
@NonNull memoryCache,//內(nèi)存資源緩存
@NonNull bitmapPool,//內(nèi)存Bitmap緩存
@NonNull arrayPool,//數(shù)據(jù)緩存池
@NonNull requestManagerRetriever,//RequestManager管理集合
@NonNull connectivityMonitorFactory,//網(wǎng)絡(luò)監(jiān)聽器的生產(chǎn)工廠
int logLevel,//log日志等級映穗,默認(rèn)為Log.info=4
@NonNull defaultRequestOptionsFactory,//默認(rèn)的RequestOptions生產(chǎn)工廠
@NonNull defaultTransitionOptions,//默認(rèn)的資源展現(xiàn)過渡配置容器窖张,,默認(rèn)map大小為0
@NonNull defaultRequestListeners,//在圖像加載時的監(jiān)聽器數(shù)組蚁滋,默認(rèn)數(shù)組大小為0
boolean isLoggingRequestOriginsEnabled,//是否需要請求日志
boolean isImageDecoderEnabledForBitmaps,//在安卓P或更高版本進行解碼bitmap
int hardwareBitmapFdLimit,//700,
int minHardwareDimension){//128,
/***部分代碼省略***/
}
}
Glide
構(gòu)造的時候需要構(gòu)造Engine
:
public class Engine
implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
/***部分代碼省略***/
public Engine(
MemoryCache memoryCache,//內(nèi)存緩存
DiskCache.Factory diskCacheFactory,//磁盤緩存
//磁盤緩存執(zhí)行器宿接,該線程池的核心線程數(shù)和最大線程數(shù)為1
GlideExecutor diskCacheExecutor,
//檢索資源尚未在緩存中,該線程池的核心線程數(shù)和最大線程數(shù)為cpu內(nèi)核數(shù)量辕录,最大為4
GlideExecutor sourceExecutor,
////newScheduledThreadPool睦霎,核心線程數(shù)為0,用來執(zhí)行網(wǎng)絡(luò)操作
GlideExecutor sourceUnlimitedExecutor,
//加載動畫線程池走诞,加載動畫圖像的幀時使用副女,尤其是GitDrawable,該線程池的核心線程數(shù)和最大線程數(shù)為1或2(cpu內(nèi)核數(shù)量>=4)
GlideExecutor animationExecutor,
//活動資源是否允許被保留蚣旱,默認(rèn)為false
boolean isActiveResourceRetentionAllowed) {
/***部分代碼省略***/
}
/***部分代碼省略***/
}
在閱讀源碼的時候Glide.java
的構(gòu)造方法碑幅,除過基礎(chǔ)的賦值操作之前就剩下大量的注冊。注冊的所有組件都由Registry
進行管理塞绿。
Register
Register
:管理組件注冊以擴展或替換Glide
的默認(rèn)加載沟涨,解碼和編碼邏輯。
public class Registry {
public static final String BUCKET_GIF = "Gif";
public static final String BUCKET_BITMAP = "Bitmap";
public static final String BUCKET_BITMAP_DRAWABLE = "BitmapDrawable";
private static final String BUCKET_PREPEND_ALL = "legacy_prepend_all";
private static final String BUCKET_APPEND_ALL = "legacy_append";
//維護{@link ModelLoader}的有序放置以及它們處理的模型和數(shù)據(jù)類型,從最高優(yōu)先級到最低優(yōu)先級的順序异吻。
private final ModelLoaderRegistry modelLoaderRegistry;
//編碼數(shù)據(jù)容器裹赴,有序列表
private final EncoderRegistry encoderRegistry;
//包含能夠解碼任意數(shù)據(jù)類型的{@link ResourceDecoder}的有序列表
//分為從最高優(yōu)先級解碼器到最低優(yōu)先級解碼器的任意資源類型。
private final ResourceDecoderRegistry decoderRegistry;
//包含能夠編碼任意資源*類型的{@link ResourceEncoder}的有序列表诀浪。
private final ResourceEncoderRegistry resourceEncoderRegistry;
//DataRewinder的生產(chǎn)工廠
private final DataRewinderRegistry dataRewinderRegistry;
//注冊和轉(zhuǎn)化ResourceTranscoder
private final TranscoderRegistry transcoderRegistry;
//包含能夠解析圖像標(biāo)題的{@link ImageHeaderParser}的無序列表棋返。
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
//維護“模型+資源”類的高速緩存到一組已注冊資源類的集合,這些資源類是可以從模型類中解碼的資源類的子類笋妥。
private final ModelToResourceClassCache modelToResourceClassCache = new ModelToResourceClassCache();
//維護數(shù)據(jù),資源和轉(zhuǎn)碼類的緩存
private final LoadPathCache loadPathCache = new LoadPathCache();
//異常列表維護容器
private final Pool<List<Throwable>> throwableListPool = FactoryPools.threadSafeList();
/***部分代碼省略***/
}
下面我們看看Glide
默認(rèn)注冊的各種組件窄潭。
Encoder
Encoder:用于將數(shù)據(jù)編碼成文件春宣。
//用于將數(shù)據(jù)寫入某些持久性數(shù)據(jù)存儲的接口,例如文件
public interface Encoder<T> {
//將給定數(shù)據(jù)寫入給定輸出流嫉你,如果寫入完成月帝,則返回True
boolean encode(@NonNull T data, @NonNull File file, @NonNull Options options);
}
EncoderRegistry
:包含能夠編碼任意數(shù)據(jù)類型的Encoder
的有序列表。
DataClass | Encoder |
---|---|
ByteBuffer | ByteBufferEncoder |
InputStream | StreamEncoder |
Bitmap | BitmapEncoder |
BitmapDrawable | BitmapDrawableEncoder |
GifDrawable | GifDrawableEncoder |
//用于將數(shù)據(jù)從資源寫入某些持久性數(shù)據(jù)存儲的接口幽污,例如文件
public interface ResourceEncoder<T> extends Encoder<Resource<T>> {
//獲取對應(yīng)的策略模式
@NonNull
EncodeStrategy getEncodeStrategy(@NonNull Options options);
}
//ResourceEncoder進行資源編碼緩存枚舉
public enum EncodeStrategy {
//將資源的原始未修改數(shù)據(jù)寫入磁盤,不包括采樣和轉(zhuǎn)化
SOURCE,
//將資源的解碼嚷辅,下采樣和轉(zhuǎn)換后的數(shù)據(jù)寫入磁盤。
TRANSFORMED,
/** Will write no data. */
NONE,
}
ResourceEncoderRegistry
:包含能夠編碼任意資源類型的ResourceEncoder
的有序列表距误。
ResourceClass | ResourceEncoder |
---|---|
BitmapDrawable | BitmapDrawableEncoder |
GifDrawable | GifDrawableEncoder |
Bitmap | BitmapEncoder |
Decoder
Decoder:解碼給定的資源類型文件簸搞。
/**
*用于解碼資源的接口扁位。
*@param <T>將從中解碼資源的類型(文件,InputStream等)趁俊。
*@param <Z>解碼資源的類型(位圖域仇,可繪制等)。
*/
public interface ResourceDecoder<T, Z> {
/**
*如果此解碼器能夠使用給定的源解碼給定的源寺擂,則返回true選項暇务,否則為false。
*解碼器應(yīng)盡最大努力快速確定是否可能夠解碼數(shù)據(jù)怔软,但不應(yīng)嘗試完全讀取給定的數(shù)據(jù)垦细。
*典型的實現(xiàn)將檢查文件頭,以確保它們與解碼器期望的內(nèi)容匹配句柄(即GIF解碼器應(yīng)驗證圖像是否包含GIF標(biāo)頭塊)挡逼。
*/
boolean handles(@NonNull T source, @NonNull Options options) throws IOException;
//從給定的數(shù)據(jù)返回已解碼的資源括改;如果無法解碼任何資源,則返回null挚瘟。
//注意width和height參數(shù)僅是提示叹谁,沒有要求解碼后的資源與給定尺寸完全匹配。
//一個典型的用例將使用目標(biāo)尺寸來確定對位圖進行降采樣的量乘盖,以避免分配過多焰檩。
@Nullable
Resource<Z> decode(@NonNull T source, int width, int height, @NonNull Options options)throws IOException;
}
ResourceDecoderRegistry
:包含ResourceDecoder
的有序列表,這些列表可以將任意數(shù)據(jù)類型解碼為從最高優(yōu)先級解碼器到最低優(yōu)先級解碼器的任意資源類型订框。
Bucket | DataClass | ResourceClass | ResourceDecoder |
---|---|---|---|
Bitmap | ByteBuffer | Bitmap | ByteBufferBitmapImageDecoderResourceDecoder |
Bitmap | InputStream | Bitmap | InputStreamBitmapImageDecoderResourceDecoder |
Bitmap | ParcelFileDescriptor | Bitmap | VideoDecoder |
Bitmap | AssetFileDescriptor | Bitmap | VideoDecoder |
Bitmap | Bitmap | Bitmap | UnitBitmapDecoder |
Bitmap | GifDecoder | Bitmap | GifFrameResourceDecoder |
Bucket | DataClass | ResourceClass | ResourceDecoder |
---|---|---|---|
BitmapDrawables | ByteBuffer | BitemapDrawable | BitmapDrawableDecoder |
BitmapDrawables | InputStream | BitemapDrawable | BitmapDrawableDecoder |
BitmapDrawables | ParcelFileDescriptor | BitemapDrawable | BitmapDrawableDecoder |
Bucket | DataClass | ResourceClass | ResourceDecoder |
---|---|---|---|
Gif | ByteBuffer | GifDrawable | ByteBufferGifDecoder |
Bucket | DataClass | ResourceClass | ResourceDecoder |
---|---|---|---|
All | Uri | Drawable | ResourceDrawableDecoder |
All | Uri | Bitmap | ResourceBitmapDecoder |
All | File | File | FileDecoder |
All | Drawable | Drawable | UnitDrawableDecoder |
Transcoder
Transcoder:將一種類型的資源轉(zhuǎn)碼為另一種類型的資源析苫。
/**
* 將一種類型的資源轉(zhuǎn)碼為另一種類型的資源。
* @param <Z> 要被轉(zhuǎn)碼的資源類型
* @param <R> 需要轉(zhuǎn)成的資源類型
*/
public interface ResourceTranscoder<Z, R> {
//將給定資源轉(zhuǎn)碼為新資源類型并返回新資源穿扳。
@Nullable
Resource<R> transcode(@NonNull Resource<Z> toTranscode, @NonNull Options options);
}
TranscoderRegistry
:該類允許ResourceTranscoder
在它們之間進行轉(zhuǎn)換的類中進行注冊和檢索衩侥。
ResourceClass | TranscoeClass | ResourceTranscoder |
---|---|---|
Bitmap | BitmapDrawable | BitmapDrawableTranscoder |
Bitmap | byte[] | BitmapBytesTranscoder |
Drawable | byte[] | DrawableBytesTranscoder |
GifDrawable | byte[] | GifDrawableBytesTranscoder |
ModelLoader
ModelLoader
是Glide
比較核心的類,主要是用來加載數(shù)據(jù)源Model
中的數(shù)據(jù)矛物。
一般加載資源類型有Bitmap
茫死、String(網(wǎng)絡(luò)圖片、本地圖片履羞、資源圖片)
峦萎、Uri(網(wǎng)絡(luò)圖片、本地圖片忆首、資源圖片)
爱榔、URL(網(wǎng)絡(luò)圖片)
、Integer(資源圖片)
和File(本地文件)
等糙及。
Glide
為每中資源類型設(shè)計了對應(yīng)的ModelLoaderFactory
详幽,每種ModelLoaderFactory
對應(yīng)一種ModleLoader
。
資源類型可以相互轉(zhuǎn)化,比如String
轉(zhuǎn)URL
唇聘。所以ModelLoader
內(nèi)部也是可以相互進行代理版姑。
//com.bumptech.glide.load.model.MultiModelLoaderFactory
//通過僅在以下位置創(chuàng)建加載器來避免堆棧溢出遞歸地創(chuàng)建模型加載器
//遞歸請求(如果尚未在鏈中的早期創(chuàng)建)。例如:Uri加載程序可以轉(zhuǎn)換為另一個模型雳灾,而后者又可以轉(zhuǎn)換回Uri漠酿。
//盡管原始Uri加載程序不會提供給中間模型加載程序,其他的Uri裝載程序也會谎亩。
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {//去重
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
//遞歸調(diào)用MultiModelLoaderFactory.build
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
//調(diào)用ModelLoaderFactory.build
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
比如ModelClass
是 String
類型炒嘲,我們遍歷找到 StreamFactory
:
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
@NonNull
@Override
public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
這個時候有會進行一次遞歸:
//com.bumptech.glide.load.model.MultiModelLoaderFactory
public synchronized <Model, Data> ModelLoader<Model, Data> build(
@NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass) {
try {
List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
boolean ignoredAnyEntries = false;
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {//去重
ignoredAnyEntries = true;
continue;
}
if (entry.handles(modelClass, dataClass)) {
alreadyUsedEntries.add(entry);
//遞歸調(diào)用MultiModelLoaderFactory.build
loaders.add(this.<Model, Data>build(entry));
alreadyUsedEntries.remove(entry);
}
}
if (loaders.size() > 1) {//數(shù)量大于1的時候構(gòu)建MultiModelLoader存儲loaders
return factory.build(loaders, throwableListPool);
} else if (loaders.size() == 1) {
return loaders.get(0);
} else {
//如果遞歸導(dǎo)致沒有可用的加載程序,請避免崩潰匈庭。該斷言應(yīng)該捕獲完全未處理的類型夫凸,遞歸可能意味著未在某處處理子類型進入堆棧,這通常是可以的阱持。
if (ignoredAnyEntries) {
return emptyModelLoader();
} else {
throw new NoModelLoaderAvailableException(modelClass, dataClass);
}
}
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
所以我們根據(jù)Model
的類型從注冊到Glide
的ModelLoaderFactory
中尋找匹配項夭拌,這個查找是按照添加的順序進行遍歷。找到對應(yīng)的ModelLoaderFactory
在生成 ModelLoader
的時候可能會繼續(xù)尋找它的代理的ModelLoader
衷咽,直到不需要代理為止鸽扁,我們會對這個結(jié)果去重然后如果結(jié)果數(shù)量大于1那么會生成MultiModelLoader
存儲這一組 ModelLoader
。如果這組中的 ModelLoader
中還包括 MultiModelLoader
那么這個 MultiModelLoader
內(nèi)還會有一組 ModelLoader
镶骗。
下面為ModelLoader涉及到的類:
DataSource
:表示某些檢索到的數(shù)據(jù)的來源桶现。
public enum DataSource {
//表示數(shù)據(jù)可能是從設(shè)備本地檢索的,盡管可能已經(jīng)是通過可能已從遠(yuǎn)程源獲取數(shù)據(jù)的內(nèi)容提供者獲得的鼎姊。
LOCAL,
//表示從設(shè)備以外的遠(yuǎn)程源檢索到數(shù)據(jù)骡和。
REMOTE,
//表示從設(shè)備緩存中檢索的數(shù)據(jù)未經(jīng)修改。
DATA_DISK_CACHE,
//表示數(shù)據(jù)是從設(shè)備上緩存中的已修改內(nèi)容中檢索到的相寇。
RESOURCE_DISK_CACHE,
//表示已從內(nèi)存緩存中檢索數(shù)據(jù)慰于。
MEMORY_CACHE,
}
DataFetcher
:加載資源的數(shù)據(jù)。
/**
* 懶惰地檢索可用于加載資源的數(shù)據(jù)唤衫。
* @param <T> 要加載的數(shù)據(jù)類型 (InputStream, byte[], File etc).
*/
public interface DataFetcher<T> {
//獲取可以從中解碼資源的數(shù)據(jù)婆赠。
//沒有數(shù)據(jù)的時候調(diào)用,異步線程執(zhí)行佳励,io不會阻塞
void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
//清理或回收此數(shù)據(jù)獲取器使用的任何資源休里。
void cleanup();
//取消任務(wù)
void cancel();
//返回此訪存器將嘗試獲取的數(shù)據(jù)的類。
@NonNull
Class<T> getDataClass();
//返回要處理的資源數(shù)據(jù)類型
@NonNull
DataSource getDataSource();
}
ModelLoaderFactory
:用于為給定模型類型創(chuàng)建ModelLoader
的接口植兰。使用設(shè)計模式之工廠模式份帐,讓每一個ModelLoader
對應(yīng)一個工廠璃吧。
public interface ModelLoaderFactory<T, Y> {
//為此模型類型構(gòu)建一個具體的ModelLoader楣导。
@NonNull
ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);
//一種生命周期方法,該工廠即將被替換時將被調(diào)用畜挨。
void teardown();
}
ModelLoader
:用于將任意復(fù)雜的數(shù)據(jù)模型轉(zhuǎn)換為具體的數(shù)據(jù)類型筒繁。
/**
*工廠接口噩凹,用于將任意復(fù)雜的數(shù)據(jù)模型轉(zhuǎn)換為具體的數(shù)據(jù)類型,DataFetcher可以使用來獲取由模型。
*此接口有兩個目標(biāo):
*1.將特定模型轉(zhuǎn)換為可以被解碼為資源毡咏。
*2.允許將模型與視圖的尺寸組合以獲取模型的資源具體尺寸驮宴。
*這不僅避免了必須在xml和代碼中重復(fù)尺寸,以便確定具有不同密度的設(shè)備上視圖的大小呕缭,
*但也允許您使用布局權(quán)重或通過編程方式放置視圖的尺寸而不會強迫您獲取通用資源大小堵泽。
*您獲取的資源越小,使用的帶寬和電池壽命越少恢总,并且越低每個資源的內(nèi)存占用量迎罗。
*
*@param <Model> 模型的類型。
*@param <Data> 可以使用的數(shù)據(jù)類型ResourceDecoder來解碼資源片仿。
*/
public interface ModelLoader<Model, Data> {
//返回可以解碼model的LoadData來進行資源解碼
//注意-如果無法返回有效的數(shù)據(jù)提取程序(例如纹安,如果模型的URL為空),然后可以從此方法返回空數(shù)據(jù)獲取程序砂豌。
@Nullable
LoadData<Data> buildLoadData(@NonNull Model model, int width, int height, @NonNull Options options);
//當(dāng)前的數(shù)據(jù)模型是否能被處理
boolean handles(@NonNull Model model);
}
LoadData
:加載的資源的Key
和對應(yīng)加載該資源的DataFetcher
的包裝對象厢岂。
//包含一組標(biāo)識負(fù)載源的keys,指向等效數(shù)據(jù)的備用緩存鍵以及DataFetcher阳距,可用于獲取在緩存中找不到的數(shù)據(jù)塔粒。
class LoadData<Data> {
public final Key sourceKey;//數(shù)據(jù)源標(biāo)識
public final List<Key> alternateKeys;//備用的標(biāo)識
public final DataFetcher<Data> fetcher;//可用于加載資源的數(shù)據(jù)
public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData(
@NonNull Key sourceKey,
@NonNull List<Key> alternateKeys,
@NonNull DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}
ModelLoaderRegistry
:維護ModelLoader
的有序放置以及它們處理的模型和數(shù)據(jù)類型,從最高優(yōu)先級到最低優(yōu)先級的順序。
ModelClass | DataClass | ModelLoaderFactory |
---|---|---|
Bitmap | Bitmap | UnitModelLoader.Factory |
GifDecoder | GifDecoder | UnitModelLoader.Factory |
File | ByteBuffer | ByteBufferFileLoader.Factory |
File | InputStream | FileLoader.StreamFactory |
File | ParcelFileDescriptor | FileLoader.FileDescriptorFactory |
File | File | UnitModelLoader.Factory |
int | InputStream | ResourceLoader.StreamFactory |
int | ParcelFileDescriptor | ResourceLoader.FileDescriptorFactory |
int | Uri | ResourceLoader.UriFactory |
int | AssetFileDescriptor | ResourceLoader.AssetFileDescriptorFactory |
Integer | InputStream | ResourceLoader.StreamFactory |
Integer | ParcelFileDescriptor | ResourceLoader.FileDescriptorFactory |
Integer | Uri | ResourceLoader.UriFactory |
Integer | AssetFileDescriptor | ResourceLoader.AssetFileDescriptorFactory |
String | InputStream | DataUrlLoader.StreamFactory |
String | InputStream | StringLoader.StreamFactory |
String | ParcelFileDescriptor | StringLoader.FileDescriptorFactory |
String | AssetFileDescriptor | StringLoader.AssetFileDescriptorFactory |
Uri | InputStream | DataUrlLoader.StreamFactory |
Uri | InputStream | HttpUriLoader.Factory() |
Uri | InputStream | AssetUriLoader.StreamFactory |
Uri | InputStream | MediaStoreImageThumbLoader.Factory |
Uri | InputStream | MediaStoreVideoThumbLoader.Factory |
Uri | InputStream | UriLoader.StreamFactory |
Uri | InputStream | UrlUriLoader.StreamFactory |
Uri | ParcelFileDescriptor | AssetUriLoader.FileDescriptorFactory |
Uri | ParcelFileDescriptor | UriLoader.FileDescriptorFactory |
Uri | AssetFileDescriptor | UriLoader.AssetFileDescriptorFactory |
Uri | File | MediaStoreFileLoader.Factory |
Uri | Uri | UnitModelLoader.Factory |
URL | InputStream | UrlLoader.StreamFactory |
GlideUrl | InputStream | HttpGlideUrlLoader.Factory |
byte[] | InputStream | ByteArrayLoader.StreamFactory |
byte[] | ByteBuffer | ByteArrayLoader.ByteBufferFactory |
Drawable | Drawable | UnitModelLoader.Factory |
我們現(xiàn)在就用 String
類型的網(wǎng)絡(luò)圖片地址的 Model
來看一下:
尋找處理 String
的 ModelLoader
:
DataUrlLoader.StreamFactory.build=DataUrlLoader
StringLoader.StreamFactory.build
=MultiModelLoaderFactory(Uri.class, InputStream.class)=MultiModelLoader
==>DataUrlLoader.StreamFactory=DataUrlLoader
==>HttpUriLoader.Factory=HttpUriLoader
==>==>MultiModelLoaderFactory(Uri.class, ParcelFileDescriptor.class)
==>==>==>AssetUriLoader.FileDescriptorFactory=AssetUriLoader
==>==>==>UriLoader.FileDescriptorFactory=AssetUriLoader
==>==>==>AssetUriLoader(去重)
==>AssetUriLoader.StreamFactory=AssetUriLoader
==>MediaStoreImageThumbLoader.Factory=MediaStoreImageThumbLoader
==>MediaStoreVideoThumbLoader.Factory=MediaStoreVideoThumbLoader
==>UriLoader.StreamFactory=AssetUriLoader
==>UrlUriLoader.StreamFactory
==>==>MultiModelLoaderFactory(GlideUrl.class, InputStream.class)
==>==>==>HttpGlideUrlLoader.Factory=HttpGlideUrlLoader
StringLoader.FileDescriptorFactory.build
=MultiModelLoaderFactory(Uri.class, ParcelFileDescriptor.class)=MultiModelLoader
==>AssetUriLoader.FileDescriptorFactory=AssetUriLoader
==>UriLoader.FileDescriptorFactory=UriLoader
StringLoader.AssetFileDescriptorFactory.build
=MultiModelLoaderFactory(Uri.class, AssetFileDescriptor.class)
=UriLoader.AssetFileDescriptorFactory=UriLoader
接下來對這一組 ModelLoader
進行處理娄涩,過濾掉無法處理 Model
數(shù)據(jù)的 ModelLoader
窗怒。其中 MultiModelLoader
中只要有一個 ModelLoader
能處理 Model
數(shù)據(jù), 那么這個 MultiModelLoader
就可以不被過濾蓄拣。
StringLoader.StreamFactory=MultiModelLoader[7]
StringLoader.FileDescriptorFactory=MultiModelLoader[2]
StringLoader.AssetFileDescriptorFactory=UriLoader
Transformation
Glid-Transformation文檔:https://muyangmin.github.io/glide-docs-cn/doc/transformations.html
Transformation:用于在實現(xiàn)資源上執(zhí)行任意轉(zhuǎn)換的類扬虚。
//唯一標(biāo)識某些數(shù)據(jù)放置的接口。
public interface Key {
String STRING_CHARSET_NAME = "UTF-8";
Charset CHARSET = Charset.forName(STRING_CHARSET_NAME);
//將所有唯一標(biāo)識信息添加到給定的摘要中球恤。
void updateDiskCacheKey(@NonNull MessageDigest messageDigest);
@Override
boolean equals(Object o);
@Override
int hashCode();
}
//用于在實現(xiàn)資源上執(zhí)行任意轉(zhuǎn)換的類
//equals和hashCode來標(biāo)識內(nèi)存緩存中的轉(zhuǎn)換
//updateDiskCacheKey來標(biāo)識磁盤中的轉(zhuǎn)換緩存辜昵。
public interface Transformation<T> extends Key {
//轉(zhuǎn)換給定資源并返回轉(zhuǎn)換后的資源
@NonNull
Resource<T> transform(@NonNull Context context, @NonNull Resource<T> resource, int outWidth, int outHeight);
}
DataRewinder
DataRewinder:對數(shù)據(jù)的游標(biāo)進行重置,也就是所謂的倒帶咽斧。
這個邏輯在上一篇文章 Android-Universal-Image-Loader源碼分析 中也有講到過堪置,我們拿到數(shù)據(jù)流之后可能會從它的頭部信息中獲取一些圖片本身的參數(shù),然后我們再將數(shù)據(jù)流寫入文件緩存的時候要重置數(shù)據(jù)流的游標(biāo)保證寫入的數(shù)據(jù)完整张惹。
public interface DataRewinder<T> {
interface Factory<T> {
@NonNull
DataRewinder<T> build(@NonNull T data);
@NonNull
Class<T> getDataClass();
}
//將包裝的數(shù)據(jù)回退到實例化此對象時的位置舀锨,然后返回重新包裝的數(shù)據(jù)
@NonNull
T rewindAndGet() throws IOException;
//當(dāng)不再需要該復(fù)卷機并可以對其進行清理時調(diào)用蛮位。
void cleanup();
}
Glide
中的 DataRewinder
模塊設(shè)計也使用的是工廠模式逊谋。
DataClass | DataRewinder | Factory | 添加時機 |
---|---|---|---|
InputStream | InputStreamRewinder | InputStreamRewinder.Factory | Glide構(gòu)造中注冊 |
ByteBuffer | ByteBufferRewinder | ByteBufferRewinder.Factory | Glide構(gòu)造中注冊 |
ALL | DefaultRewinder | DEFAULT_FACTORY | 默認(rèn)值,不用添加 |
Transition
Transition
不是由Glide
進行注冊的蚁廓,而是我們在業(yè)務(wù)代碼中按照需要添加的。它作為Glide
組件的一種替蔬,所以我們放在這里來進行介紹告私。
Glide-Transition文檔:https://muyangmin.github.io/glide-docs-cn/doc/transitions.html
Glide
提供了很多的過渡效果,用戶可以手動地應(yīng)用于每個請求承桥。Glide
的內(nèi)置過渡以一致的方式運行驻粟,并且將根據(jù)加載圖像的位置在某些情況下避免運行。
//包裝視圖的目標(biāo)將能夠提供所有必要的參數(shù)并開始過渡凶异。
public interface Transition<R> {
//包含視圖的接口蜀撑,該視圖公開了運行各種類型的必需的方法
//Glide中所有ViewTarget的子類都實現(xiàn)了該接口
interface ViewAdapter {
//返回包裝的view
View getView();
//返回在視圖中顯示的當(dāng)前可繪制對象;如果不存在這樣的可繪制對象剩彬,則返回null(或無法檢索)屯掖。
@Nullable
Drawable getCurrentDrawable();
//設(shè)置當(dāng)前可繪制對象(通常是動畫可繪制對象)以在包裝視圖中顯示。
void setDrawable(Drawable drawable);
}
//從當(dāng)前正在使用的上一個Drawable進行動畫處理在給定視圖中顯示襟衰,
//如果在運行過渡過程中將新資源放在視圖中贴铜,則為True,
//如果調(diào)用者需要手動將當(dāng)前資源放在視圖上瀑晒,則為false绍坝。
boolean transition(R current, ViewAdapter adapter);
}
//一個工廠類,可以根據(jù)請求的狀態(tài)產(chǎn)生不同的Transition
public interface TransitionFactory<R> {
//返回一個新的Transition
//isFirstResource如果這是要加載到目標(biāo)中的第一個資源苔悦,則為True轩褐。
Transition<R> build(DataSource dataSource, boolean isFirstResource);
}
Glide使用
我們上面從業(yè)務(wù)代碼中自定義的@GlideModule
注解講到了Glide
的構(gòu)造,以及Glide
中模塊&組件的注冊以及其對應(yīng)的功能和處理邏輯玖详。
接下來我們繼續(xù)根據(jù)業(yè)務(wù)代碼中Glide
最常用的代碼把介,來講述Glide
的其它部分。
Glide.with(context)
.load(myUrl)
.into(imageView);
這段代碼中有我們?nèi)齻€業(yè)務(wù)邏輯參數(shù):context
蟋座、myUrl
拗踢、imageView
。
context
:本次加載圖片的上下文環(huán)境向臀;myUrl
:本次需要加載圖片的地址巢墅,也叫數(shù)據(jù);imageView
:本次需要加載圖片的View
券膀,也叫目標(biāo)君纫;
RequestManager
Glide.with(context)
根據(jù)這一行代碼我們進行分析:
//com.bumptech.glide.Glide.java
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
Glide.with(context)
獲取的是RequestManager
。
//用于管理和啟動對Glide的請求的類芹彬⌒钏瑁可以使用活動,片段和連接性生命周期事件智能地停止舒帮,啟動和重新啟動請求会喝。
//通過實例化一個新對象或利用Activity和Fragment生命周期內(nèi)置的處理功能進行檢索眨唬,請對Fragment或Activity使用靜態(tài)Glide.load方法。
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
/***代碼全都省略***/
}
ComponentCallbacks2
:Android
自帶的內(nèi)存管理的接口好乐,Application
和Activity
都有實現(xiàn)。主要可以根據(jù)系統(tǒng)的內(nèi)存狀況及時調(diào)整App內(nèi)存占用瓦宜,提升用戶體驗或讓App存活更久蔚万。
LifecycleListener
:activity或者fragment的聲明周期監(jiān)聽接口
public interface LifecycleListener {
void onStart();//fragmetn或者activity的onstart
void onStop();//fragmetn或者activity的onstop
void onDestroy();//fragmetn或者activity的ondestroy
}
ModelTypes
:通俗的說就是Glide
的API。
interface ModelTypes<T> {
@NonNull
@CheckResult
T load(@Nullable Bitmap bitmap);
T load(@Nullable Drawable drawable);
T load(@Nullable String string);
T load(@Nullable Uri uri);
T load(@Nullable File file);
T load(@RawRes @DrawableRes @Nullable Integer resourceId);
T load(@Nullable URL url);
T load(@Nullable byte[] model);
T load(@Nullable Object model);
}
RequestManager的獲取
//com.bumptech.glide.manager.RequestManagerRetriever.java
//Application的生命周期單獨管理
private volatile RequestManager applicationManager;
//一個是support包临庇,一個是Android的api反璃,RequestManagerFragment的臨時存儲
//RequestManagerFragment用來管理RequestManager和同步生命周期
final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments = new HashMap<>();
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>();
public RequestManager get(@NonNull 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) {//FragmentActivity
return get((FragmentActivity) context);
} else if (context instanceof Activity) {//Activity
return get((Activity) context);
} else if (context instanceof ContextWrapper//找到context的basecontext
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
我們用Fragment
來舉例看看RequestManager
的獲取(Activity
和FragmentActivity
的實現(xiàn)都類似):
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getContext(),"fragment必須被attch假夺,不能被destroy");
if (Util.isOnBackgroundThread()) {//如果應(yīng)用在后臺淮蜈,那么切換為Application
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//獲取當(dāng)前上下文中的SupportRequestManagerFragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//獲取SupportRequestManagerFragment的RequestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//給新創(chuàng)建的SupportRequestManagerFragment添加RequestManager
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//先看看當(dāng)前的FragmentManager中有沒有SupportRequestManagerFragment,有就復(fù)用
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//pendingSupportRequestManagerFragments已卷,只是一個臨時性的存儲,因為每次put之后必定會remove
//因為add之后立即進行findFragmentByTag是獲取不到的梧田,所以需要臨時存儲
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//創(chuàng)建新的SupportRequestManagerFragment
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
//如果fragment已經(jīng)展示,那么添加的SupportRequestManagerFragment也要同步生命周期
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
//添加臨時存儲
pendingSupportRequestManagerFragments.put(fm, current);
//創(chuàng)建的SupportRequestManagerFragment添加到FragmentManager中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//刪除臨時存儲
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
其它功能
執(zhí)行\(zhòng)取消一個請求侧蘸、暫停所有請求裁眯、重啟所有請求等對請求的標(biāo)記,這部分會在Request
詳細(xì)講解讳癌。在說Request
之前先說一下Request
需要為誰加載目標(biāo)資源穿稳。
Target
Target
:在聲明周期內(nèi)Glide加載資源回調(diào)接口;
BaseTarget
:用于加載Resource
的基礎(chǔ)Target
大多數(shù)方法的基本或空實現(xiàn)晌坤;
TargetView
:為Bitmap添加到View上提供了默認(rèn)實現(xiàn)逢艘,
public interface Target<R> extends LifecycleListener {
//表示我們希望資源保持其原始的未修改寬度和/或高度
int SIZE_ORIGINAL = Integer.MIN_VALUE;
//開始加載
void onLoadStarted(@Nullable Drawable placeholder);
//加載失敗
void onLoadFailed(@Nullable Drawable errorDrawable);
//資源加載完成時將調(diào)用的方法。
void onResourceReady(@NonNull R resource, @Nullable Transition<? super R> transition);
//在取消負(fù)載及其資源釋放時調(diào)用
void onLoadCleared(@Nullable Drawable placeholder);
//一種檢索此目標(biāo)大小的方法
void getSize(@NonNull SizeReadyCallback cb);
//如果給定的回調(diào)仍保留骤菠,則將其從待處理集中刪除
void removeCallback(@NonNull SizeReadyCallback cb);
//設(shè)置為此目標(biāo)保留的當(dāng)前請求
void setRequest(@Nullable Request request);
//檢索對此目標(biāo)的當(dāng)前請求
@Nullable
Request getRequest();
}
RequestBuilder
Glide.with(context)
.load(myUrl)
我們繼續(xù)看下一行load
代碼:
//com.bumptech.glide.RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
RequestBuilder
通用類它改,可以處理通用資源類型的設(shè)置選項和啟動負(fù)載。GlideRequest
雖然繼承了RequestBuilder
但內(nèi)部的實現(xiàn)都是super
調(diào)用父類的方法商乎。
//com.bumptech.glide.RequestManager.java
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
asDrawable
創(chuàng)建一個請求資源類型為 Drawable.class
的 RequestBuilder
搔课。
//com.bumptech.glide.RequestBuilder.java
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
接下來設(shè)置這個 RequestBuilder
的 Model
為String.class
的 myUrl
。
Glide.with(context)
.load(myUrl)
.into(imageView);
RequestBuilder
的 into
方法是 Glide
加載圖片的最后一步截亦。
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
//下面是根據(jù)view修改requestOptions的屬性
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
into
方法主要根據(jù) View
的屬性重構(gòu)造 requestOptions
爬泥, 并生成 DrawableImageViewTarget
。
ViewTarget的生成
public class GlideContext extends ContextWrapper {
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
/***部分代碼省略***/
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
transcodeClass
實際就是上面的 resourceClass
也就是 Drawable.class
崩瓤。
接下來我們繼續(xù)看它的 into
方法:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
//檢測是否設(shè)置了model
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//創(chuàng)建請求,Executors.mainThreadExecutor()標(biāo)識callback在主線程
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
//如果target上的當(dāng)前請求與上一次的相同袍啡,且請求緩存磁盤并且上一個請求為完成時使用上一個請求
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//如果請求已完成,請重新開始却桶,以確保重新發(fā)送結(jié)果境输,觸發(fā)RequestListeners和Targets蔗牡。
//如果請求失敗,將重新開始重新啟動請求嗅剖,使它有另一個完成的機會辩越。
//如果請求已經(jīng)正在運行,我們可以讓它繼續(xù)運行而不會受到干擾信粮。
if (!Preconditions.checkNotNull(previous).isRunning()) {
//使用上一個請求而不是新請求來進行優(yōu)化黔攒,例如跳過設(shè)置占位符,跟蹤和取消跟蹤目標(biāo)并獲取視圖尺寸在單獨的請求中完成强缘。
previous.begin();
}
return target;
}
//清除target之前的請求
requestManager.clear(target);
//給target設(shè)置新的請求
target.setRequest(request);
//target同步requestmanager的聲明周期督惰,并將request添加到請求列表
requestManager.track(target, request);
return target;
}
private boolean isSkipMemoryCacheWithCompletePreviousRequest(
BaseRequestOptions<?> options, Request previous) {
return !options.isMemoryCacheable() && previous.isComplete();
}
好吧,終于看見加載圖片的請求了~旅掂!
這里會根據(jù)之前構(gòu)建的一些參數(shù)來生成 Request
赏胚,然后和 ViewTarget
的 Request
進行比較決定是否重用之前的 Request
,或者清除之前的 Request
商虐。
Request
Request
:為Target
加載資源的請求觉阅。
public interface Request {
void begin();//開始異步加載
//防止從以前的請求中加載任何位圖,釋放由此持有的任何資源請求秘车,并將該請求標(biāo)記為具有已取消留拾。
void clear();
//暫停請求
void pause();
//如果此請求正在運行并且尚未完成或失敗,則返回true鲫尊。
boolean isRunning();
//如果請求成功完成痴柔,則返回true
boolean isComplete();
//如果請求已清除,則返回true
boolean isCleared();
//判斷兩個請求是否相同
boolean isEquivalentTo(Request other);
}
Request的創(chuàng)建
Request request = buildRequest(target, targetListener, options, callbackExecutor);
target
: 我們對ImageView
包裝之后的的ViewTarget
疫向;targetListener
:默認(rèn)為null
咳蔚;options
:為重新根據(jù)view
屬性修改之后的requestOptions
;callbackExecutor
: 回調(diào)的線程池搔驼,在主線程中執(zhí)行回調(diào)谈火;
生成的Request
實例為 SingleRequest
,它是專門為了Target
而加載資源的舌涨。
如果需要設(shè)置處理 Error
的請求那么會在 Request
外包一層生成 ErrorRequestCoordinator
糯耍。
ErrorRequestCoordinator
:運行單個主要的Request直到完成,然后僅在單個主要請求失敗的情況下后退錯誤請求囊嘉;
如果需要設(shè)置處理縮略圖的請求那么會在 Request
外包一層生成 ThumbnailRequestCoordinator
温技。
ThumbnailRequestCoordinator
:一個協(xié)調(diào)器,用于協(xié)調(diào)兩個單獨的Request
扭粱,它們同時加載圖像的小縮略圖版本和圖像的完整尺寸版本舵鳞。
整個Glide
中 Request
的實現(xiàn)類就SingleRequest
、ErrorRequestCoordinator
和 ThumbnailRequestCoordinator
一共三個琢蛤。
SingleRequest
SingleRequest
不僅實現(xiàn)了 Request
蜓堕,還實現(xiàn)了SizeReadyCallback
和 ResourceCallback
接口抛虏。
SizeReadyCallback
:當(dāng)目標(biāo)確定其大小時必須調(diào)用的回調(diào)。對于固定尺寸的目標(biāo)可以同步調(diào)用套才。
public interface SizeReadyCallback {
void onSizeReady(int width, int height);
}
ResourceCallback
:偵聽資源加載成功完成或失敗的回調(diào)迂猴。
public interface ResourceCallback {
//成功加載資源時調(diào)用。
void onResourceReady(Resource<?> resource, DataSource dataSource);
//當(dāng)資源無法成功加載時調(diào)用背伴。
void onLoadFailed(GlideException e);
//返回通知單個請求時要使用的鎖
Object getLock();
}
上面我們閱讀into
代碼的時候知道了 request
的執(zhí)行是調(diào)用了RequestManager.track
沸毁。
//com.bumptech.glide.RequestManager
//對request進行追蹤,默認(rèn)的new RequestTracker()挂据;
private final RequestTracker requestTracker;
//對target進行追蹤
private final TargetTracker targetTracker = new TargetTracker();
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
//添加到RequestManger的target追蹤容器中
targetTracker.track(target);
//運行request
requestTracker.runRequest(request);
}
TargetTracker
: 保留當(dāng)前對有效的一組Target儿普,并轉(zhuǎn)發(fā)生命周期事件崎逃。RequestTracker
:用于跟蹤,取消和重新啟動進行中眉孩,已完成和失敗的請求的類个绍。
Request
已經(jīng)講述了一部分了,之前 RequestManger
部分對于 Request
的請求浪汪、清除巴柿、暫停、重新啟動沒有詳細(xì)講述死遭,現(xiàn)在可以開始^_^广恢。
Request.begin
//com.bumptech.glide.manager.RequestTracker
public void runRequest(@NonNull Request request) {
//添加到請求容器中
requests.add(request);
//如果當(dāng)前的RequestManager不處于暫停狀態(tài),那么直接開始請求
if (!isPaused) {
request.begin();
} else {
//否則延遲該請求
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
接下來看SingleRequest
的 begin
方法呀潭,開始真正的獲取資源钉迷。
//com.bumptech.glide.manager.RequestTracker
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();//如果已經(jīng)進行了回調(diào),那么拋異常
stateVerifier.throwIfRecycled();//如果任務(wù)已經(jīng)被回收钠署,那么拋異常
startTime = LogTime.getLogTime();
if (model == null) {
//校驗寬高是否有效
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
//詳細(xì)的日志級別進行記錄
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {//如果任務(wù)已經(jīng)處于運行狀態(tài)糠聪,那么拋異常
throw new IllegalArgumentException("Cannot restart a running request");
}
//如果任務(wù)已經(jīng)執(zhí)行完成,那么直接回調(diào)加載資源
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
//重新啟動既未完成也未運行的請求谐鼎,可以視為新請求并可以從頭開始再次運行舰蟆。
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);//內(nèi)部有請求資源的實現(xiàn)方法調(diào)用
} else {
target.getSize(this);//重新回調(diào)onSizeReady方法
}
//加載占位圖
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
已知加載的ViewTarget
的寬高才會進一步加載資源。
//com.bumptech.glide.request.SingleRequest
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();//如果任務(wù)已經(jīng)被回收狸棍,那么拋異常
synchronized (requestLock) {//加鎖身害,進行數(shù)據(jù)請求
if (status != Status.WAITING_FOR_SIZE) {//必須size確定
return;
}
status = Status.RUNNING;//進入運行狀態(tài)
//在加載資源之前,對Target的大小應(yīng)用乘數(shù)草戈。對于加載縮略圖或避免加載大量資源非常有用题造。默認(rèn)為1f。
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
loadStatus =
engine.load(
glideContext,
model,//myUrl
requestOptions.getSignature(),//緩存簽名猾瘸,默認(rèn)為EmptySignature
this.width,
this.height,
requestOptions.getResourceClass(),//不設(shè)置decode的話界赔,默認(rèn)為Object
transcodeClass,//Drawable.class
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
/***部分代碼省略***/
}
}
Engine.load
//com.bumptech.glide.load.engine.Engine
public <R> LoadStatus load(/***部分代碼省略***/) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key = //構(gòu)造緩存的key
keyFactory.buildKey(
model,//數(shù)據(jù)myUrl
signature,//簽名
width,
height,
transformations,//轉(zhuǎn)變
resourceClass,//Object.class
transcodeClass,//Drawable.class
options);
EngineResource<?> memoryResource;
synchronized (this) {
//先從緩存中加載
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(/***部分代碼省略***/);
}
}
//回調(diào)可以并發(fā)丢习,不需要進行鎖操作
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
內(nèi)存緩存加載
private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {//如果不允許使用內(nèi)存,那么直接返回null
return null;
}
EngineResource<?> active = loadFromActiveResources(key);//獲取活躍的資源
if (active != null) {
return active;
}
EngineResource<?> cached = loadFromCache(key);//獲取內(nèi)存緩存中的資源
if (cached != null) {
return cached;
}
return null;
}
ActiveResources
ActiveResources
:利用持有Resource
弱引用的HashMap
來進行數(shù)據(jù)保存淮悼。
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
- 添加操作:有兩個觸發(fā)點咐低,一個是從
cache
中讀取的時候會將資源添加到ActiveResources
。另一個獲取資源完成的時候會添加到ActiveResources
袜腥。它們有一個相同的點都在使用資源的時候添加到ActiveRsources
见擦。 - 刪除操作:
EngineResource
在內(nèi)部有一個引用計數(shù)器,每次被獲取的時候都會進行acquire
自加羹令。被釋放的時候也是會自減鲤屡。當(dāng)引用計數(shù)為0的時候,會被ActivityResources
釋放福侈,并添加到MemoryCache
中酒来。
MemoryCache
MemoryCache
:同樣使用 LRU 算法,實現(xiàn)類為LruResourceCache
肪凛,它提供了一個ResourceRemovedListener
接口堰汉,當(dāng)有資源從MemoryCache
中被移除時會回調(diào)其中的方法,Engine
中接收到這個消息后就會進行Bitmap
的回收操作伟墙。
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true, true, key, this);
}
return result;
}
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
- 添加操作:當(dāng)資源被
ActiveResources
釋放的時候翘鸭; - 刪除操作:當(dāng)資源被
ActivityResource
獲取的時候;
以上兩點保證了 ActiveResource
和 MemoryCache
中的資源是不會有重復(fù)的戳葵。
DecodeJob
DecodeJob
:負(fù)責(zé)從緩存的數(shù)據(jù)或原始源解碼資源就乓,并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼。
接下來的部分會詳細(xì)講解上圖的每個部分拱烁。
磁盤緩存加載
等待創(chuàng)建或者是獲取已經(jīng)存在的加載狀態(tài)档址。
private <R> LoadStatus waitForExistingOrStartNewJob(/***部分代碼省略***/) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);//從hashmap中獲取加載key的工作引擎
if (current != null) {//如果已經(jīng)存在那么直接返回
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = //通過添加和刪除負(fù)載的回調(diào)并在負(fù)載完成時通知回調(diào)來管理負(fù)載的類。
engineJobFactory.build(/***部分代碼省略***/);
DecodeJob<R> decodeJob = //負(fù)責(zé)從緩存的數(shù)據(jù)或原始源解碼資源邻梆,并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼守伸。
decodeJobFactory.build(/***部分代碼省略***/);
jobs.put(key, engineJob);//添加到hashmap中
engineJob.addCallback(cb, callbackExecutor);//添加回調(diào)信息
engineJob.start(decodeJob);//開始工作
return new LoadStatus(cb, engineJob);
}
選擇執(zhí)行線程,開始執(zhí)行任務(wù):
//com.bumptech.glide.load.engine.EngineJob
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//根據(jù)磁盤緩存策略浦妄,判斷使用哪個線程池執(zhí)行任務(wù)
GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
在diskCacheExecutor
線程池中執(zhí)行 DecoceJob
尼摹。
//com.bumptech.glide.load.engine.DecodeJob
public void run() {
/***部分代碼省略***/
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {//如果任務(wù)已經(jīng)取消,那么進行失敗回調(diào)
notifyFailed();
return;
}
runWrapped();//開始執(zhí)行任務(wù)
} catch (CallbackException e) {
//如果不受Glide控制的回調(diào)引發(fā)異常剂娄,則應(yīng)避免使用Glide下面的特定調(diào)試邏輯蠢涝。
} catch (Throwable t) {
//通知失敗,進行失敗回調(diào)阅懦,拋出異常
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
runWrapped
為 run
實現(xiàn)的包裝和二。
//com.bumptech.glide.load.engine.DecodeJob
private enum RunReason {
//初始化
INITIALIZE,
//切換執(zhí)行服務(wù)
SWITCH_TO_SOURCE_SERVICE,
//獲取數(shù)據(jù)之后的資源編解碼
DECODE_DATA,
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
再講下一步數(shù)據(jù)從磁盤的獲取之前,先看一下我們默認(rèn)的磁盤策略:
public static final DiskCacheStrategy AUTOMATIC =
new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(
boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
@Override
public boolean decodeCachedResource() {
return true;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
- 如果數(shù)據(jù)時
REMOTE
類型那么可以進行緩存耳胎; - 如果是解碼資源而且
Key
不是源數(shù)據(jù)的Key
惯吕,資源為DATA_DISK_CACHE
的話惕它,那么這個資源可以緩存; - 可以緩存解碼資源废登;
- 可以緩存解碼數(shù)據(jù)淹魄;
從上面我們可以了解到緩存有數(shù)據(jù)緩存
和 解碼后的數(shù)據(jù)緩存
。
我們接著分析 runWrapped
方法內(nèi)調(diào)用的 getNextStage
:
//com.bumptech.glide.load.engine.DecodeJob
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE://AUTOMATIC默認(rèn)為true堡距,所以初始化的第一步應(yīng)該檢測Stage.RESOURCE_CACHE
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE://AUTOMATIC默認(rèn)為true甲锡,所以初始化的下一步應(yīng)該檢測Stage.DATA_CACHE
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:////如果用戶選擇僅從緩存中檢索資源,則從源跳過加載羽戒。否則應(yīng)該檢測Stage.SOURCE
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
知道了Stage
類型接著分析 runWrapped
方法內(nèi)調(diào)用的 getNextStage
:
//com.bumptech.glide.load.engine.DecodeJob
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE://解碼之后的資源緩存
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE://源數(shù)據(jù)的緩存
return new DataCacheGenerator(decodeHelper, this);
case SOURCE://數(shù)據(jù)源
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
DataFetcherGenerator
: 注冊ModelLoaders
和Model
的數(shù)據(jù)提取構(gòu)造器缤沦。
interface DataFetcherGenerator {
//嘗試使用一個新的DataFetcher,則返回true已啟動易稠,否則為false缸废。
boolean startNext();
//嘗試取消當(dāng)前正在運行的提取程序
void cancel();
}
ResourceCacheGenerator
、DataCacheGenerator
和 SourceGenerator
除過實現(xiàn)了DataFetcherGenerator
還實現(xiàn)了 DataCallback
接口缩多。
DataCallback
: 數(shù)據(jù)已加載且可用時或加載時必須調(diào)用的回調(diào)失敗呆奕。
interface DataCallback<T> {
//如果加載成功养晋,則使用加載的數(shù)據(jù)進行調(diào)用衬吆;如果加載失敗,則使用null進行調(diào)用绳泉。
void onDataReady(@Nullable T data);
//加載失敗時調(diào)用
void onLoadFailed(@NonNull Exception e);
}
獲取到DataFetcherGenerator
構(gòu)造器之后執(zhí)行 startNext
逊抡,判斷是否下一步能開始加載數(shù)據(jù)。
//com.bumptech.glide.load.engine.EngineJob
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {//執(zhí)行當(dāng)前數(shù)據(jù)構(gòu)造的判斷是否能加載數(shù)據(jù)
stage = getNextStage(stage);//切換數(shù)據(jù)狀態(tài)
currentGenerator = getNextGenerator();//獲取新的數(shù)據(jù)構(gòu)造器
if (stage == Stage.SOURCE) {//需要需要進行源數(shù)據(jù)加載那么切換執(zhí)行線程
reschedule();
return;
}
}
//如果已經(jīng)完成或者取消零酪,而且沒開始獲取數(shù)據(jù)冒嫡。那么通知失敗
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
//否則,生成器將開始新的加載四苇,我們希望將其調(diào)回onDataFetcherReady孝凌。
}
ResourceCacheGenerator
ResourceCacheGenerator
:從包含降采樣/轉(zhuǎn)換后的資源緩存文件中獲取數(shù)據(jù)。
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {
/***部分代碼省略***/
@Override
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();//能處理Model(myUrl)的LoadData列表月腋,返回其Key
if (sourceIds.isEmpty()) {//如果沒有蟀架,那么標(biāo)示Model(myUrl)這個數(shù)據(jù)類型無法處理,直接返回false
return false;
}
//獲取glide中注冊的榆骚,能加載Model(url)片拍,能解碼resourceClass(Drawable),能轉(zhuǎn)碼transcodeClass(Object)的組件集合
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {//如果沒有適配的組件集合妓肢,而且轉(zhuǎn)碼的類型為文件的話那么拋異常捌省。因為File都不能轉(zhuǎn)碼那么根本無法磁盤緩存
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
throw new IllegalStateException(/***部分代碼省略***/);
}
while (modelLoaders == null || !hasNextModelLoader()) {//如果還有modelLoader
resourceClassIndex++;
//雙重判斷使LoadData列表進行完全遍歷
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
//ResourceCacheKey用于下采樣和轉(zhuǎn)換的資源數(shù)據(jù)的緩存密鑰+任何請求的簽名。
currentKey =
new ResourceCacheKey(
helper.getArrayPool(),
sourceId,//modelLoaddata
helper.getSignature(),//簽名
helper.getWidth(),//寬
helper.getHeight(),//高
transformation,//轉(zhuǎn)換
resourceClass,//解碼
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
//判斷Resource是否有緩存碉钠,如果有那么加載這個緩存文件數(shù)據(jù)
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);//獲取能加載文件的ModelLoader列表
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//ModelLoader根據(jù)cacheFile纲缓,width卷拘,height,options構(gòu)造LoadData
loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
//該LoadData是否被Glide進行加載過色徘,是否可用
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;//如果可用恭金,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
@Override
public void cancel() {
LoadData<?> local = loadData;
if (local != null) {
local.fetcher.cancel();
}
}
@Override//數(shù)據(jù)已經(jīng)準(zhǔn)備好,標(biāo)識數(shù)據(jù)源為RESOURCE_DISK_CACHE
public void onDataReady(Object data) {
cb.onDataFetcherReady(
sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
}
@Override//加載失敗褂策,標(biāo)識數(shù)據(jù)源為RESOURCE_DISK_CACHE
public void onLoadFailed(@NonNull Exception e) {
cb.onDataFetcherFailed(currentKey, e, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE);
}
}
DataCacheGenerator
DataCacheGenerator
: 構(gòu)造來自包含原始未修改源數(shù)據(jù)的緩存文件横腿。
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {
/***部分代碼省略***/
@Override
public boolean startNext() {
//如果還有modelLoader
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
//cacheKyes(helper.getCacheKeys)在構(gòu)造的時候傳入,和ResourceCacheGenerator的相同
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
//原始源數(shù)據(jù)的緩存鍵和任何請求的簽名斤寂。
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
//判斷Resource是否有緩存耿焊,如果有那么加載這個緩存文件數(shù)據(jù)
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);//獲取能加載文件的ModelLoader列表
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//ModelLoader根據(jù)cacheFile,width遍搞,height罗侯,options構(gòu)造LoadData
loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
//該LoadData是否被Glide進行加載過,是否可用
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;//如果可用溪猿,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
@Override
public void onDataReady(Object data) {//數(shù)據(jù)已經(jīng)準(zhǔn)備好钩杰,標(biāo)識數(shù)據(jù)源為DATA_DISK_CACHE
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
@Override
public void onLoadFailed(@NonNull Exception e) {//加載失敗,標(biāo)識數(shù)據(jù)源為DATA_DISK_CACHE
cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
}
}
可以看出來 ResourceCacheGenerator
的實現(xiàn)其實和 DataCacheGenerator
的實現(xiàn)邏輯很相似诊县,只不過是緩存的 Key
不同而已讲弄。
源數(shù)據(jù)請求
SourceGenerator
: 根據(jù)磁盤緩存策略,可以先將源數(shù)據(jù)編碼后寫入磁盤依痊,然后再加載從緩存文件而不是直接返回避除。
class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,
DataFetcherGenerator.FetcherReadyCallback {
private DataCacheGenerator sourceCacheGenerator;//磁盤緩存的數(shù)據(jù)構(gòu)造器
private Object dataToCache;//需要緩存的數(shù)據(jù)
private DataCacheKey originalKey;//磁盤緩存的key
/***部分代碼省略***/
@Override
public boolean startNext() {
if (dataToCache != null) {//是否已經(jīng)擁有緩存的數(shù)據(jù)
Object data = dataToCache;
dataToCache = null;
cacheData(data);//進行據(jù)緩存,緩存后sourceCacheGenerator不為空
}
//由SourceGenerator轉(zhuǎn)到DataCacheGenerator(網(wǎng)絡(luò)轉(zhuǎn)磁盤)
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {//如果還有LoadData(model,width胸嘁,height瓶摆,options構(gòu)造LoadData)
loadData = helper.getLoadData().get(loadDataListIndex++);
//fetcher.getDataSource()是否可以進行磁盤緩存
if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {//該LoadData是否被Glide進行加載過,是否可用
started = true;//如果可用性宏,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
/***部分代碼省略***/
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {//獲取dataToCache對應(yīng)的解碼器群井,StreamEncoder
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());//生成數(shù)據(jù)源的key
helper.getDiskCache().put(originalKey, writer);//將writer寫入到磁盤緩存中,對應(yīng)的key為originalKey
} finally {
loadData.fetcher.cleanup();
}
//構(gòu)造原始數(shù)據(jù)緩存構(gòu)造器毫胜,轉(zhuǎn)到磁盤讀取數(shù)據(jù)
sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
@Override//數(shù)據(jù)加載成功回調(diào)书斜,date類型為InputStream
public void onDataReady(Object data) {DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
//如果數(shù)據(jù)不為空,而且可以緩存那么機進行SourceGenerator轉(zhuǎn)DataCacheGenerator
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();//切換線程指蚁,切換完之后currentGenerator還是SourceGenerator
} else {//否早直接將數(shù)據(jù)進行回調(diào)
cb.onDataFetcherReady(loadData.sourceKey,data,loadData.fetcher,loadData.fetcher.getDataSource(),originalKey);
}
}
@Override//數(shù)據(jù)加載失敗回調(diào)
public void onLoadFailed(@NonNull Exception e) {
cb.onDataFetcherFailed(originalKey, e, loadData.fetcher, loadData.fetcher.getDataSource());
}
@Override//DataCacheGenerator的數(shù)據(jù)加載成功回調(diào)菩佑,data類型為ByteBuffer
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
// 此數(shù)據(jù)提取程序?qū)奈募屑虞d并提供錯誤的數(shù)據(jù)源,因此請覆蓋使用原始提取程序的數(shù)據(jù)源
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
@Override//DataCacheGenerator的數(shù)據(jù)加載失敗回調(diào)
public void onDataFetcherFailed(Key sourceKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource) {
cb.onDataFetcherFailed(sourceKey, e, fetcher, loadData.fetcher.getDataSource());
}
}
SourceGenerator
凝化、DataCacheGenerator
和 ResourceCacheGenerator
的 startNext
方法都會尋找合適的 ModelLoader
構(gòu)建對應(yīng)的 LoadData
來加載數(shù)據(jù)稍坯。
現(xiàn)在我們的 Model
是 myUrl
一個網(wǎng)絡(luò)圖片地址,在沒有加載過之前ResourceCacheGenerator
和 DataCacheGenerator
當(dāng)然是沒有緩存的,所以我們這里用SourceGenerator
加載一個網(wǎng)絡(luò)圖片的過程在詳細(xì)講述一下 startNext
中怎么獲取LoadData
進行數(shù)據(jù)加載(其他兩個都實現(xiàn)都類似)瞧哟。
需要注意的是
Data
已經(jīng)是從磁盤緩存讀出來的ByteBuffer
混巧,但是DataSource
還是REMOTE
。
數(shù)據(jù)解碼和轉(zhuǎn)碼
無論加載的緩存還是其他的數(shù)據(jù)成功后都會回調(diào) onDataFetcherReady
勤揩,然后進行數(shù)據(jù)解碼僚饭。
//com.bumptech.glide.load.engine.DecodeJob
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;//url
this.currentData = data;//ByteBuffer
this.currentFetcher = fetcher;//ByteBufferFetcher
this.currentDataSource = dataSource;//REMOTE
this.currentAttemptingKey = attemptedKey;//url
//如果當(dāng)前線程不是主線程括丁,那么切換到主線程執(zhí)行數(shù)據(jù)解碼(調(diào)用decodeFromRetrievedData)
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();//進行數(shù)據(jù)解碼
} finally {
GlideTrace.endSection();
}
}
}
接下來的步驟:
-
decodeFromRetrievedData
中調(diào)用decodeFromData
糠排; -
decodeFromData
中調(diào)用decodeFromFetcher
辛慰; -
decodeFromFetcher
獲取Registry.getLoadPath
以及Registry.getRewinder
;
最終是為了獲得 DecodePaht
和 LoadPath
负蠕。
解碼&漸變&轉(zhuǎn)碼
LoadPath
:一個或多個DecodePath
來進行數(shù)據(jù)的解碼蛙埂;
//com.bumptech.glide.Registry
@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass,//ByteBuffer.class
@NonNull Class<TResource> resourceClass,//Object.class
@NonNull Class<Transcode> transcodeClass) {//Drawable.class
LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
return null;
} else if (result == null) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
//可能無法將給定的類型解碼或轉(zhuǎn)碼為所需的類型數(shù)據(jù)類。
if (decodePaths.isEmpty()) {
result = null;
} else {
result = new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
DecodePath
:嘗試從給定的數(shù)據(jù)類型解碼和轉(zhuǎn)碼資源類型遮糖;
//com.bumptech.glide.Registry
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass,
@NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
//GifDrawable,Bitmap,BitmapDrawable
List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
//transcoder=DrawableBytesTranscoder
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(
dataClass,//ByteBuffer
registeredResourceClass,//GifDrawable/Bitmap/BitmapDrawable
registeredTranscodeClass,//Drawable/Drawable/Drawable
//ByteBufferGifDecoder/ByteBufferBitmapDecoder/BitmapDrawableDecoder
decoders,
///UnitTranscoder/BitmapDrawableTranscoder/UnitTranscoder
transcoder,
throwableListPool);
decodePaths.add(path);
}
}
return decodePaths;
}
- 解碼的時候調(diào)用
DecodePath.decode
方法绣的,該方法會循環(huán)遍歷內(nèi)部的decoders
列表中的ResourceDecoder
如果ByteBufferBitmapDecoder.decode
解碼成功(Bitmap
)直接返回。 - 繼續(xù)回調(diào)判斷是或否進行
transform
如果需要那么DecodeJob.onResourceDecoded
進行transform
進行資源的漸變欲账,漸變之后重新構(gòu)造緩存的Key
將再次進行Encoder
屡江。 - 然后再進行
BitmapDrawableTranscoder.transcode
進行轉(zhuǎn)碼得到LazyBitmapDrawableResource
。 - 回到
notifyEncodeAndRelease
方法進行資源成功回調(diào)赛不,以及判斷判斷是否需要Encoder
進行磁盤緩存惩嘉,回調(diào)編碼成功釋放資源。
至此整個資源的獲取過程完成俄删,最后我們獲取出了Drawable
宏怔。
加載&釋放資源
DecodeJob
進行
notifyComplete
: 回調(diào)EngineJob.onResourceReady
;進行
onEncodeComplete
:釋放當(dāng)前的DecodeJob
;
EngineJob
- 進行
notifyCallbacksOfResult
奏路,回調(diào)incrementPendingCallbacks
進行資源的引用+1 畴椰; - 回調(diào)
Engine.onEngineJobComplete
; - 執(zhí)行
CallResourceReady
任務(wù)鸽粉,回調(diào)SingleRequest.onResourceReady
斜脂,并進行資源的引用+1; - 執(zhí)行
decrementPendingCallbacks
進行資源的引用-1触机;
Engine
- 將資源添加到
activeResource
; - 移除當(dāng)前
jobs
中對應(yīng)的EngineJob
;
SingleRequest
- 判斷&校驗帚戳,獲取資源;
- 進行
target.onResourceReadt
將資源加載到View
上儡首。
總結(jié)
因為上一篇文章是 Android-Universal-Image-Loader源碼分析 片任,所以這里主要是結(jié)合 ImageLoader
來和 Glide
進行比較。
功能
-
Glide
默認(rèn)的網(wǎng)絡(luò)請求和ImageLoader
相同都是HttpUrlConnection
蔬胯; -
Glide
和ImageLoader
都可以添加自定義的網(wǎng)絡(luò)請求比如OkHttp
; -
Glide
和ImageLoader
都支持在圖片加載前獲取圖片的數(shù)據(jù)(圖片的寬对供、高)。 -
Glide
會根據(jù)請求時會根據(jù)當(dāng)前context
的聲明周期進行Request
的管理(粒度為Application
和Activity
)。這個功能是ImageLoader
不具備的产场,ImageLoader
只能停止一個請求鹅髓,或者停止所有請求。 -
Glide
和ImageLoader
都具有加載默認(rèn)圖京景、加載失敗備用圖的功能窿冯。 -
Glide
具備加載縮略圖的功能,這個功能是ImageLoader
不具備的确徙。 -
Glide
可以設(shè)置請求時候的優(yōu)先級醒串,雖然說這個請求優(yōu)先級不是十分嚴(yán)格僅僅是指導(dǎo)來做請求的優(yōu)化處理,但這個功能是ImageLoader
不具備的鄙皇。 -
Glide
加載圖片的數(shù)據(jù)可支持多種類型厦凤,ImageLoader
只支持String
。 -
Glide
和ImageLoader
都可以自定義配置圖片加載庫使用的download
育苟、decoder
较鼓、executor
、cache
等违柏,只不過Glide
對自定義模塊配置更加的方便以及粒度更細(xì)博烂。
緩存
-
ImageLoader
緩存的key
只能根據(jù)圖片地址進行處理生成。Glide
的原始數(shù)據(jù)的磁盤緩存的Key
是由url
和signature
組成漱竖,資源圖片的緩存(磁盤緩存和內(nèi)存緩存)的Key
是由圖片的(寬禽篱、高、資源類型馍惹、資源轉(zhuǎn)換類型躺率、資源解碼類型、簽名万矾、model
以及option
)組成而且簽名可以由我們自己的request
進行設(shè)置悼吱。 -
Gilde
可以進行請求時的設(shè)置跳過緩存,或者進行signature
設(shè)置防止緩存失效問題良狈。這個是ImageLoader
不具備的功能后添。 -
Glide
進行漸變處理的圖片會被再次緩存,這個功能是ImageLoader
不具備的薪丁。 -
Glide
的內(nèi)存緩存分為兩級MemoryCache
和ActiveResource
遇西,MemoryCache
和ImageLoader
相同都是使用LruCache
,ActiveResource
對正在使用的圖片做了弱引用严嗜,防止使用中的資源
被LRU
算法回收掉粱檀。
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦漫玄!茄蚯!