前言
若想把握 Glide 圖片加載的精髓, 首先要理清 Glide 圖片加載的一次流程
// 這里便是與 Glide 3+ 的不同
RequestOptions options = new RequestOptions()
.placeholder(R.drawable.loading);
// 它需要一個(gè)
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
好的, 可以看到 Glide 的使用方式極為簡(jiǎn)單, 但往往越是簡(jiǎn)單的背后, 越是隱藏了復(fù)雜的實(shí)現(xiàn), 接下來我們就一步一步的分析 Glide 4.9 的一次加載流程
一. with
public class Glide implements ComponentCallbacks2 {
public static RequestManager with(@NonNull Context context) {
// 1. 調(diào)用了 getRetriever 獲取一個(gè) RequestManagerRetriever
// 2. 調(diào)用了 RequestManagerRetriever.get 獲取一個(gè) RequestManager 描述一個(gè)圖片加載請(qǐng)求的管理者
return getRetriever(context).get(context);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 1.1 調(diào)用了 Glide.get 獲取 Glide 對(duì)象
// 1.2 通過 Glide 對(duì)象獲取一個(gè) RequestManagerRetriever
// 這個(gè) Retriever 用來獲取一個(gè) RequestManager 對(duì)象, 可以參考 Android Framework 源碼中的 SystemRetriever
return Glide.get(context).getRequestManagerRetriever();
}
}
好的, 可以看到 Glide.with 操作, 主要做了兩件事情
- 通過 Glide.getRetriever 獲取一個(gè) RequestManagerRetriever 對(duì)象, 它描述為請(qǐng)求管理的獲取器
- 調(diào)用 Glide.get 獲取 Glide 對(duì)象
- 調(diào)用 Glide.getRequestManagerRetriever, 獲取 RequestManagerRetriever 對(duì)象
- 調(diào)用 getRequestManagerRetriever.get 獲取一個(gè) RequestManager
接下來我們一步一步的看, 首先是獲取 RequestManagerRetriever
一) 獲取 Glide 對(duì)象
從上面的分析可只, RequestManagerRetriever 是通過 Glide.getRequestManagerRetriever 獲取到的, 因此需要先獲取 Glide 對(duì)象的實(shí)例, 因此我們先看看這個(gè) Glide 是如何構(gòu)造的
public class Glide implements ComponentCallbacks2 {
private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
private static volatile boolean isInitializing;
private static void checkAndInitializeGlide(@NonNull Context context) {
if (isInitializing) {
// 拋出二次初始的異常
}
isInitializing = true;
// 進(jìn)行初始化操作
initializeGlide(context);
isInitializing = false;
}
private static void initializeGlide(@NonNull Context context) {
// 創(chuàng)建了一個(gè) GlideBuilder() 實(shí)例傳入 initializeGlide, 很顯然是為了初始化 Glide 對(duì)象
// 接下來我們就看看它是如何初始化 Glide 的
initializeGlide(context, new GlideBuilder());
}
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
// 1. 獲取 @GlideModule 注解驅(qū)動(dòng)生成的 GeneratedAppGlideModuleImpl 和 GeneratedAppGlideModuleFactory 類
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
......
// 2. 嘗試從注解生成的 annotationGeneratedModule 中獲取 RequestManager 的構(gòu)造工廠對(duì)象
RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
// 3. 向 Glide 的 Builder 中添加這個(gè)請(qǐng)求管理器的構(gòu)造工廠
builder.setRequestManagerFactory(factory);
......
// 4. 構(gòu)建 Glide 的實(shí)體對(duì)象
Glide glide = builder.build(applicationContext);
......
// 5. 向 Application 中注冊(cè)一個(gè)組件的回調(diào), 用于檢測(cè)系統(tǒng) Config 改變和內(nèi)存占用量低的信號(hào)
applicationContext.registerComponentCallbacks(glide);
// 保存在靜態(tài)的成員變量中
Glide.glide = glide;
}
}
從上的代碼可以看到 Glide 對(duì)象是進(jìn)程間單例的, 初次創(chuàng)建會(huì)調(diào)用 initializeGlide 方法, 其處理事務(wù)如下
- 首先找尋 @GlideModule 注解生成類(這里省略了它的實(shí)現(xiàn))
- 然后將一個(gè) RequestManagerFactory 添加到 GlideBuilder 內(nèi)部
- 后便構(gòu)建了一個(gè) Glide 的對(duì)象
我們主要關(guān)注一下 Glide 對(duì)象創(chuàng)建的過程
public final class GlideBuilder {
// 管理線程池的引擎
private Engine engine;
// 1. 線程池
private GlideExecutor sourceExecutor;
private GlideExecutor diskCacheExecutor;
private GlideExecutor animationExecutor;
// 2. 內(nèi)存的緩存策略
private MemorySizeCalculator memorySizeCalculator;
private MemoryCache memoryCache;
// 3. 享元復(fù)用池
private BitmapPool bitmapPool;
private ArrayPool arrayPool;
// 4. 磁盤緩存和請(qǐng)求構(gòu)建工廠
private DiskCache.Factory diskCacheFactory;
private RequestManagerFactory requestManagerFactory;
@NonNull
Glide build(@NonNull Context context) {
/*
1. 線程池
*/
// 1.1 網(wǎng)絡(luò)操作使用線程池
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
// 1.2 磁盤緩存使用的線程池
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
// 1.3 執(zhí)行動(dòng)畫的線程池
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
/*
2. 內(nèi)存緩存
*/
// 2.1 描述一個(gè)內(nèi)存的計(jì)算器, 智能加載圖片的大小, 判斷其需要的內(nèi)存空間
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
// 2.2 LRU 資源內(nèi)存緩存
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
// 2.3 LRU 的 Bitmap 復(fù)用池
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
// 2.4 LRU 數(shù)組復(fù)用池
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
/*
3 磁盤緩存的工廠
*/
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
// 4. RequestManagerRetriever 對(duì)象
RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);
// 5. 構(gòu)建了一個(gè)負(fù)責(zé)管理線程池與緩存的執(zhí)行引擎
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
......
// 6. 創(chuàng)建了 Glide 對(duì)象
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions,
defaultRequestListeners,
......);
}
}
好的, 可以看到 Glide 對(duì)象的構(gòu)建過程異常的復(fù)雜, 筆者調(diào)整了部分的數(shù)據(jù), 它們的流程如下
- 構(gòu)建線程池
- 根據(jù)不同的任務(wù)特性, 構(gòu)建了不同的線程池
- 構(gòu)建內(nèi)存緩存策略
- 內(nèi)存計(jì)算器
- LRU 資源內(nèi)存緩存
- 數(shù)組對(duì)象復(fù)用池
- Bitmap 復(fù)用池
- 創(chuàng)建了一個(gè) RequestManagerRetriever 描述請(qǐng)求管理對(duì)象的獲取器
- 構(gòu)建引擎類, 用于組織和調(diào)度線程池和緩存
- 創(chuàng)建 Glide 對(duì)象
Glide 的創(chuàng)建流程如下
Glide 的創(chuàng)建
public class Glide implements ComponentCallbacks2 {
private final Registry registry;
Glide(......) {
// 1 將 Builder 中的線程池, 緩存池等保存
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
// 2. 使用 registry 注冊(cè) Glide 需要的 Encoder 與 Decoder
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
// 創(chuàng)建注冊(cè)器
registry = new Registry();
// 注冊(cè)圖片頭解析器
registry.register(new DefaultImageHeaderParser());
......
// 3. 構(gòu)建一個(gè) Glide 的上下文, 方便在請(qǐng)求時(shí)獲取全局?jǐn)?shù)據(jù)
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
Glide 對(duì)象的創(chuàng)建如下
- 將 GlideBuilder 中的數(shù)據(jù)導(dǎo)入
- 構(gòu)建一個(gè) registry, 注冊(cè)了眾多的編解碼器
- 構(gòu)建了上下文對(duì)象, 保存可能會(huì)用到的數(shù)據(jù)
二) 獲取 RequestManagerRetriever
在創(chuàng)建 GlideBuilder.build 中, 我們看到了它 new 了一個(gè) RequestManagerRetriever 對(duì)象并且傳遞到了 Glide 對(duì)象內(nèi)部, 于是通過 Glide.getRequestManagerRetriever 就可以很方便的獲取到 RequestManagerRetriever 這個(gè)對(duì)象了
接下來我們看看如何通過 RequestManagerRetriever.get() 獲取 RequestManager 對(duì)象
三) 獲取 RequestManager
public class RequestManagerRetriever implements Handler.Callback {
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)) {
// 1. 若在主線程 并且 Context 不為 Application
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
// 不斷的查找其 BaseContext, 判斷是否能夠與 FragmentActivity/Activity 等目標(biāo)匹配
return get(((ContextWrapper) context).getBaseContext());
}
}
// 2. 若不在 MainThread 或 context 為 Application 的類型, 則使用 ApplicationManager
return getApplicationManager(context);
}
}
可以看到 RequestManagerRetriever.get 方法會(huì)判斷 Context 的類型
- 若在主線程并且不為 Application 類型的 Context 則找尋其依賴的 Activity
- 若非主線程或?yàn)?Application 類型的 Context, 則使用 ApplicationManager
為什么要優(yōu)先找到 Activity 呢, 這么做是何用意呢?, 我們帶著問題去看看參數(shù)為 Activity 的 get 的重載方法
1. 獲取 Activity 的 RequestManager
public class RequestManagerRetriever implements Handler.Callback {
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
// 若非主線程, 直接獲取 Application 類型的 RequestManager
return get(activity.getApplicationContext());
} else {
// 不可在 activity 銷毀時(shí)執(zhí)行加載
assertNotDestroyed(activity);
// 獲取其 FragmentManager
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 1. 從 Activity 中獲取一個(gè) RequestManagerFragment, 用于監(jiān)管 Activity 的聲明周期
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
// 2. 獲取 Fragment 中保存的當(dāng)前頁面的請(qǐng)求管理器
RequestManager requestManager = current.getRequestManager();
// 3. 不存在則創(chuàng)建一個(gè)請(qǐng)求管理器保存在 RequestManagerFragment 中
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
// 返回這個(gè)請(qǐng)求管理器
return requestManager;
}
// 描述一個(gè)即將被 FragmentManager 添加的 RequestManagerFragment 緩存
final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments =
new HashMap<>();
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 2.1 嘗試從 FragmentManager 中獲取這個(gè) Fragment
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
// 2.2 不存在則添加一個(gè)
if (current == null) {
// 2.3 從 pendingRequestManagerFragments 緩存中獲取一個(gè)
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
// 2.3.1 創(chuàng)建并更新到緩存
current = new RequestManagerFragment();
......
// 2.3.2 添加到等待被添加的緩存中
// 因?yàn)樘砑拥?FragmentManager 有延遲, 用這種方式防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 對(duì)象添加到 Activity 中
pendingRequestManagerFragments.put(fm, current);
// 2.3.3 添加到 FragmentManager 中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
// 2.3.4 添加到 FragmentManager 成功, 通過 Handler 移除這個(gè)緩存
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
}
好的, 可以看到 RequestManagerRetriever 的 get 方法主要是在 Activity 頁面中添加一個(gè) RequestManagerFragment 實(shí)例, 以便用于監(jiān)聽 Activity 的生命周期, 然后給這個(gè) Fragment 注入一個(gè) RequestManager, 其處理的細(xì)節(jié)代碼中也注釋的比較詳細(xì)
- 其中有個(gè)非常引人注目的細(xì)節(jié), 考慮到將 FragmentManger 添加 Fragment 有延遲, 為了防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 添加到 FragmentManager, 因此它使用了 pendingRequestManagerFragments 進(jìn)行緩存
2. 獲取 Application 的 RequestManager
public class RequestManagerRetriever implements Handler.Callback {
private volatile RequestManager applicationManager;
private final RequestManagerFactory factory;
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context.getApplicationContext());
applicationManager = factory.build(glide, new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(), context.getApplicationContext());
}
}
}
return applicationManager;
}
}
很簡(jiǎn)單構(gòu)建了一個(gè)單例的 RequestManager, 用于處理所有的 context 類型的請(qǐng)求
三) 回顧
Glide.with 方法主要是為 Context 構(gòu)建 RequestManager , 主要流程如下
- 獲取進(jìn)程間單例的 Glide 對(duì)象
- 線程池
- 緩存池
- 編解碼器
- 獲取進(jìn)程間單例的 RequestManagerRetriever 對(duì)象
- 通過 RequestManagerRetriever 創(chuàng)建當(dāng)前 context 的 RequestManager
- 若可以綁定 Activity, 則為 Activity 添加一個(gè) RequestManagerFragment, 其內(nèi)部含有 ReqeustManager 對(duì)象, 以便后續(xù)直接根據(jù) Activity 的生命周期管控 Glide 請(qǐng)求的處理
- 若非可綁定 Activity, 則獲取一個(gè)單例的 applicationManager 專門用于處理這類請(qǐng)求
接下來我們看看這個(gè) RequestManager.load 方法
二. load
我們使用最熟悉的加載網(wǎng)絡(luò)圖片來分析這個(gè) RequestManager.load 方法
public class RequestManager implements LifecycleListener,
ModelTypes<RequestBuilder<Drawable>> {
public RequestBuilder<Drawable> load(@Nullable String string) {
// 1. 調(diào)用 asDrawable 創(chuàng)建一個(gè)目標(biāo)為 Drawable 的圖片加載請(qǐng)求
// 2. 調(diào)用 load 將要加載的資源傳入
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
}
好的, 可以看到 RequestManager.load 方法先調(diào)用了 asDrawable 構(gòu)建一個(gè) RequestBuilder, 描述一個(gè)目標(biāo)資源為 Drawable 的圖片加載請(qǐng)求
然后調(diào)用了 RequestBuilder.load 方法將加載的數(shù)據(jù)源傳入, 我們看看這個(gè) load 方法做了什么
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
// 描述加載的數(shù)據(jù)源
@Nullable private Object model;
// 描述這個(gè)請(qǐng)求是否已經(jīng)添加了加載的數(shù)據(jù)源
private boolean isModelSet;
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
}
好的, 可以看到走到這里, RequestBuilder 就構(gòu)建好了, 接下來就等待執(zhí)行這個(gè)請(qǐng)求了, 我們看看它的 RequestBuilder 的 into 方法
三. RequestBuilder.into
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
......
// 1. 根據(jù) view 的 scaleType 重構(gòu) RequestOptions
BaseRequestOptions<?> requestOptions = this;// RequestBuilder 直接繼承了 BaseRequestOptions
if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
// 1.1 克隆原 RequestOptions, 配置一個(gè) CenterCrop 的縮放選項(xiàng)
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
......
}
}
// 3. 調(diào)用 into 方法, 創(chuàng)建并且執(zhí)行請(qǐng)求
return into(
// 2. 將 View 封裝成 ViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
}
可以看到 into 方法中主要執(zhí)行了如下的步驟
- 根據(jù) ImageView 的 ScaleType 來配置 Options 選項(xiàng)
- 將 View 封裝成 ViewTarget
- 調(diào)用了重載方法 into 執(zhí)行后續(xù)構(gòu)建請(qǐng)求操作
我們以 CenterCrop 舉例, 看看它如何配置縮放效果
一) 配置 Options
public abstract class BaseRequestOptions<T extends BaseRequestOptions<T>> implements Cloneable {
public T optionalCenterCrop() {
// 1. 調(diào)用了 optionalTransform
// DownsampleStrategy 描述降采樣壓縮的策略
// CenterCrop 描述圖像變化方式
return optionalTransform(DownsampleStrategy.CENTER_OUTSIDE, new CenterCrop());
}
final T optionalTransform(@NonNull DownsampleStrategy downsampleStrategy,
@NonNull Transformation<Bitmap> transformation) {
......
// 2. 將降采樣壓縮策略添加到 options 中
downsample(downsampleStrategy);
// 3. 將圖像變化方式添加到 transformations 中
return transform(transformation, /*isRequired=*/ false);
}
public T downsample(@NonNull DownsampleStrategy strategy) {
// 2.1 調(diào)用了 set, 將降采樣策略保存到 options 中
return set(DownsampleStrategy.OPTION, Preconditions.checkNotNull(strategy));
}
private Options options = new Options();
public <Y> T set(@NonNull Option<Y> option, @NonNull Y value) {
...
// 2.2 添加到 options 緩存中
options.set(option, value);
return selfOrThrowIfLocked();
}
T transform(@NonNull Transformation<Bitmap> transformation, boolean isRequired) {
// 3.1 調(diào)用了 transform 的重載方法, 將這個(gè)圖像變化的方式作用到多種資源類型上
DrawableTransformation drawableTransformation = new DrawableTransformation(transformation, isRequired);
transform(Bitmap.class, transformation, isRequired);// Bitmap 類型的資源
transform(Drawable.class, drawableTransformation, isRequired);// Drawable類型的
......
return selfOrThrowIfLocked();
}
private Map<Class<?>, Transformation<?>> transformations = new CachedHashCodeArrayMap<>();
<Y> T transform(@NonNull Class<Y> resourceClass, @NonNull Transformation<Y> transformation, boolean isRequired) {
// 3.2 添加到了 transformations 緩存中
transformations.put(resourceClass, transformation);
return selfOrThrowIfLocked();
}
}
好的, 可以看到配置縮放選項(xiàng)的操作除了添加了圖像變化操作, 還設(shè)定了采樣方式, 分別保存在 transformations 和 options 中
我們知道 GlideContext 是在 Glide 對(duì)象構(gòu)造時(shí)一并創(chuàng)建的, 它是 Context 的裝飾者對(duì)象, 在 Application 類型的 Context 中, 添加了 Glide 相關(guān)的數(shù)據(jù), 我們先看看它是如何構(gòu)建 ViewTarget 的
二) 構(gòu)建 ViewTarget
public class GlideContext extends ContextWrapper {
private final ImageViewTargetFactory imageViewTargetFactory;
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
// 調(diào)用工廠類來創(chuàng)建一個(gè) imageView 的 ViewTarget
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
// 根據(jù)目標(biāo)編碼的類型來創(chuàng)建不同的 ViewTarget 對(duì)象, 因?yàn)槲覀儧]有 asBitmap, 因此這里為 Drawable
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 {
......
}
}
}
好的可以看到 GlideContext 中通過工廠類創(chuàng)建了 ImageView 的 ViewTarget 的, 它描述的是圖像處理結(jié)束之后, 最終要作用到的 View 目標(biāo)
三) into 重載方式
構(gòu)建好了 ViewTarge, 接下來就可以分析重載的 into 方法了, 看看它是如何構(gòu)建請(qǐng)求的
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options, Executor callbackExecutor) {
......
// 1. 調(diào)用 buildRequest 構(gòu)建了一個(gè) Glide 請(qǐng)求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
......// 處理這個(gè) ViewTarget 之前的請(qǐng)求與新請(qǐng)求的沖突
// 2. 給 ViewTarget 綁定這個(gè) Glide 請(qǐng)求
target.setRequest(request);
// 3. 調(diào)用了請(qǐng)求 RequestManager.track 方法分發(fā)請(qǐng)求
requestManager.track(target, request);
return target;
}
}
可以看到 into 的重載方法主要執(zhí)行了如下的步驟
- 將相關(guān)參數(shù)傳入構(gòu)建 Request 請(qǐng)求
- 為 ViewTarget 綁定該請(qǐng)求
- 調(diào)用 RequestManger.track 分發(fā)該請(qǐng)求
四) 回顧
RequestBuilder.into 主要做了如下的事情
- 根據(jù) ImageView 構(gòu)建采樣壓縮和圖像變化的策略保存在 Options 和 Transform 中
- 構(gòu)建 ViewTarget, 描述這個(gè)請(qǐng)求要作用的 View 對(duì)象
- 調(diào)用 into 重載方法
- 構(gòu)建 Request 對(duì)象
- 為 ViewTarget 綁定 Request
- 交由 RequestManger 執(zhí)行該請(qǐng)求
到這里我們的 Glide.with().load().into 就走完了, 最終會(huì)構(gòu)建一個(gè) Request 交由 RequestManger 分發(fā)執(zhí)行
下面我們看看 RequestManager 是如何執(zhí)行 Request 的
四. RequestManager 執(zhí)行 Request 請(qǐng)求
public class RequestManager implements LifecycleListener,
ModelTypes<RequestBuilder<Drawable>> {
private final RequestTracker requestTracker;
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
......
// 1. 執(zhí)行請(qǐng)求
requestTracker.runRequest(request);
}
}
public class RequestTracker {
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
private final List<Request> pendingRequests = new ArrayList<>();
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
// 2. 調(diào)用 request.begin 執(zhí)行任務(wù)
request.begin();
} else {
......
}
}
}
public final class SingleRequest<R> implements Request,
SizeReadyCallback,
ResourceCallback,
FactoryPools.Poolable {
public synchronized void begin() {
......
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 3. 表示尺寸準(zhǔn)備好了
onSizeReady(overrideWidth, overrideHeight);
} else {
......
}
......
}
private Engine engine;
private int width;
private int height;
public synchronized void onSizeReady(int width, int height) {
......
// 4. 調(diào)用了 Engine.load 方法構(gòu)建任務(wù)
loadStatus = engine.load(......);
......
}
}
好的, 可以看到最終調(diào)用到了 onSizeReady 去構(gòu)建可執(zhí)行任務(wù), 接下來我們就分析這一過程
一) 任務(wù)的構(gòu)建
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final Jobs jobs;
public synchronized <R> LoadStatus load(...) {
// 1. 根據(jù)傳入的參數(shù), 構(gòu)建這個(gè)請(qǐng)求的 key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// 2. 從緩存中查找 key 對(duì)應(yīng)的資源
// 2.1 嘗試從 ActiveResources 緩存中查找這個(gè) key 的緩存
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
// 若緩存存在, 則直接回調(diào) onResourceReady 處理后續(xù)操作
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
.......
return null;
}
// 2.2 嘗試從 LruResourceCache 中找尋這個(gè)資源
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
// 回調(diào) onResourceReady 處理后續(xù)操作
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
// 3. 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
// 3.1 走到這里說明這個(gè)任務(wù)已經(jīng)正在執(zhí)行了, 無需再次構(gòu)建執(zhí)行
current.addCallback(cb, callbackExecutor);
......
// 返回加載狀態(tài)即可
return new LoadStatus(cb, current);
}
// 3.2 走到這里, 說明是一個(gè)新的任務(wù)
// 3.2.1 則構(gòu)建一個(gè)新的引擎任務(wù)
EngineJob<R> engineJob = engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 3.2.2 構(gòu)建解碼任務(wù)
DecodeJob<R> decodeJob = decodeJobFactory.build(......, engineJob);
// 3.2.3 添加到任務(wù)緩存
jobs.put(key, engineJob);
......
// 3.2.4 執(zhí)行任務(wù)
engineJob.start(decodeJob);
......
}
}
好的, 可以看到 Engine.load 中的事情非常的重要
- 構(gòu)建這個(gè)請(qǐng)求的 key
-
從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
- 從 ActiveResources 緩存中查找
- 從 LruResourceCache 緩存中查找
-
從緩存中查找 key 對(duì)應(yīng)的任務(wù)
- 若存在則說明無需再次獲取資源
- 構(gòu)建任務(wù)
- 構(gòu)建引擎任務(wù) EngineJob
- 引擎的任務(wù)為解碼任務(wù) DecodeJob
- 將任務(wù)添加到緩存, 防止多次構(gòu)建
- 執(zhí)行 EngineJob
好的, 可以看到內(nèi)存緩存的處理是在 Engine 中進(jìn)行的, 若兩個(gè)內(nèi)存緩存都沒有命中, 則會(huì)構(gòu)建任務(wù)并執(zhí)行, 接下來我們看看任務(wù)的執(zhí)行過程
二) EngineJob 的執(zhí)行
class EngineJob<R> implements DecodeJob.Callback<R>,
Poolable {
private final GlideExecutor diskCacheExecutor;
private DecodeJob<R> decodeJob;
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
// 獲取線程池
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor : getActiveSourceExecutor();
// 執(zhí)行任務(wù)
executor.execute(decodeJob);
}
}
簡(jiǎn)單的交由了線程池執(zhí)行這個(gè) DecodeJob
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
@Override
public void run() {
try {
......
// 調(diào)用了 runWrapped
runWrapped();
} catch (CallbackException e) {
......
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
// 1. 獲取合適的任務(wù)場(chǎng)景
stage = getNextStage(Stage.INITIALIZE);
// 2. 獲取這個(gè)場(chǎng)景的執(zhí)行者
currentGenerator = getNextGenerator();
// 3. 執(zhí)行者執(zhí)行任務(wù)
runGenerators();
break;
......
}
}
private Stage getNextStage(Stage current) {
switch (current) {
// 1.1 判斷是否允許讀取磁盤資源緩存, 若允許則返回 Stage.RESOURCE_CACHE, 不允許則繼續(xù)找尋合適的場(chǎng)景
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
// 1.2 判斷是否允許從磁盤讀取源數(shù)據(jù)緩存, 若允許則返回 Stage.DATA_CACHE, 不允許則繼續(xù)查找
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
// 1.3 若只能允許從緩存中獲取數(shù)據(jù), 則直接 FINISH, 否則返回 Stage.SOURCE, 意為加載一個(gè)新的資源
case DATA_CACHE:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
// 2.1 資源磁盤緩存的執(zhí)行者
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// 2.2 源數(shù)據(jù)磁盤緩存的執(zhí)行者
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// 2.3 無緩存, 獲取數(shù)據(jù)的源的執(zhí)行者
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
private void runGenerators() {
......
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
// 3.1 調(diào)用 currentGenerator.startNext() 執(zhí)行當(dāng)前場(chǎng)景的任務(wù)
&& !(isStarted = currentGenerator.startNext())) {
// 3.2 若執(zhí)行失敗, 則獲取下一個(gè)場(chǎng)景繼續(xù)執(zhí)行
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
......
}
}
DecodeJob 的 runWrapper 方法主要做了如下的事務(wù)
- 通過 getNextStage 獲取 Decode 場(chǎng)景
- 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
- 調(diào)用 runGenerators 處理任務(wù)
- 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
- 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
- 最終方案重新獲取源數(shù)據(jù)
場(chǎng)景 | 場(chǎng)景描述 | 場(chǎng)景執(zhí)行器 |
---|---|---|
Stage.RESOURCE_CACHE | 從磁盤中緩存的資源中獲取數(shù)據(jù) | ResourceCacheGenerator |
Stage.DATA_CACHE | 從磁盤中緩存的源數(shù)據(jù)中獲取數(shù)據(jù) | DataCacheGenerator |
Stage.SOURCE | 重新請(qǐng)求數(shù)據(jù) | SourceGenerator |
關(guān)于 Resource 和 Data 磁盤緩存感興趣可以自行分析, 我們這里主要看看 SourceGenerator 獲取方案源數(shù)據(jù)方案
1. SourceGenerator 獲取數(shù)據(jù)流
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback<Object>,
DataFetcherGenerator.FetcherReadyCallback {
private final DecodeHelper<?> helper;
public boolean startNext() {
......
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
// 1. 從 DecodeHelper 的數(shù)據(jù)加載集合中, 獲取一個(gè)數(shù)據(jù)加載器
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
// 2. 使用加載器中 fetcher 執(zhí)行數(shù)據(jù)加載
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
}
好的, SourceGenerator 主要有兩步
- 調(diào)用 DecodeHelper.getLoadData 獲取當(dāng)前請(qǐng)求的數(shù)據(jù)加載器
- 調(diào)用加載器中的 fetcher.loadData 真正的執(zhí)行數(shù)據(jù)加載
1) 獲取數(shù)據(jù)加載器 LoadData
final class DecodeHelper<Transcode> {
private final List<LoadData<?>> loadData = new ArrayList<>();
private GlideContext glideContext;
private Object model;
private boolean isLoadDataSet;
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
// 1. 從 Glide 注冊(cè)的 register 中獲取請(qǐng)求 model 加載器
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
// 遍歷每一個(gè) modelLoaders
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
// 2. 通過 modelLoaders 構(gòu)建 loadData
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
// 添加到緩存
loadData.add(current);
}
}
}
return loadData;
}
}
它會(huì)找到一個(gè) ModelLoader 的實(shí)現(xiàn)類, 通過這個(gè)實(shí)現(xiàn)類的 handles 方法, 判斷是否可以加載這個(gè) model
這里我們的 model 以網(wǎng)絡(luò)的 URL 資源舉例, 它的實(shí)現(xiàn)類為 HttpGlideUrlLoader 我們看看它如何構(gòu)建一個(gè) LoadData 對(duì)象的
public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
@Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
GlideUrl url = model;
.....
int timeout = options.get(TIMEOUT);
// 創(chuàng)建了一個(gè) LoadData 對(duì)象, 并且實(shí)例化了一個(gè) HttpUrlFetcher 給它
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
}
好的, 可以看到對(duì)于 URL 的加載, 其 fetcher 為一個(gè) HttpUrlFetcher 的實(shí)例, 接下來我們看看數(shù)據(jù)加載的流程
2) 執(zhí)行數(shù)據(jù)加載
獲取到了數(shù)據(jù)加載器之后, SourceGenerator 的 startNext 中便會(huì)調(diào)用其 fetcher 的 loadData 執(zhí)行數(shù)據(jù)的加載了, 我們結(jié)下來便分析一下這個(gè)過程
public class HttpUrlFetcher implements DataFetcher<InputStream> {
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// 獲取網(wǎng)絡(luò)圖片, 內(nèi)部使用了 HttpConnection 實(shí)現(xiàn), 僅僅做了重定向的處理
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
// 將 inputStream 回調(diào)出去
callback.onDataReady(result);
} catch (IOException e) {
......
callback.onLoadFailed(e);
} finally {
......
}
}
}
好的, 數(shù)據(jù)加載的過程也是很簡(jiǎn)單的, HttpUrlFetcher 它使用了 HttpConnection 發(fā)起了網(wǎng)絡(luò)請(qǐng)求, 獲取了數(shù)據(jù)流, 至此數(shù)據(jù)資源的獲取就已經(jīng)完成了, 后面要做的便是最重要的數(shù)據(jù)處理了, 它通過回調(diào)的方式將 InputStream 扔了出去
最終會(huì)回溯到 DecodeJob 的 onDataFetcherReady 處理這個(gè) InputStream
2. DecodeJob 解碼數(shù)據(jù)流
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
private Key currentSourceKey;
private Object currentData;
private DataSource currentDataSource;
private DataFetcher<?> currentFetcher;
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey; // 保存數(shù)據(jù)的 key
this.currentData = data; // 保存數(shù)據(jù)實(shí)體
this.currentFetcher = fetcher; // 保存數(shù)據(jù)的獲取器
this.currentDataSource = dataSource;// 數(shù)據(jù)來源: url 為 REMOTE 類型的枚舉, 表示從遠(yuǎn)程獲取
......
if (Thread.currentThread() != currentThread) {
......
} else {
try {
// 調(diào)用 decodeFromRetrievedData 解析獲取的數(shù)據(jù)
decodeFromRetrievedData();
} finally {
......
}
}
}
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
// 1. 調(diào)用了 decodeFromData 獲取資源
resource = decodeFromData(/*HttpUrlFetcher*/currentFetcher, /*InputStream*/currentData,/*REMOTE*/ currentDataSource);
} catch (GlideException e) {
......
}
if (resource != null) {
// 2. 通知外界資源獲取成功了
notifyEncodeAndRelease(resource, currentDataSource);
} else {
......
}
}
}
DecodeJob 解碼數(shù)據(jù)流的操作如下
- 首先要將 InputStream 解碼為 Resource 類型(并非 Android Resource)
- Resource 的緩存與展示
我們先看看它是如何將數(shù)據(jù)解析成 Resource
1) 解碼 InputStream
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
......
// 調(diào)用了 decodeFromFetcher
Resource<R> result = decodeFromFetcher(data, dataSource);
......
return result;
} finally {
}
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
// 1. 獲取當(dāng)前數(shù)據(jù)類的解析器 LoadPath
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 2. 通過解析器來解析來解析數(shù)據(jù)
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
// 2.1 根據(jù)數(shù)據(jù)類型獲取一個(gè)數(shù)據(jù)重造器, 獲取的數(shù)據(jù)為 InputStream, 因此它是一個(gè) InputStreamRewinder 的實(shí)例
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// 2.2 將解析資源的任務(wù)轉(zhuǎn)移到了 LoadPath.load 方法中
return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
}
可以看到為了解析數(shù)據(jù), 首先構(gòu)建了一個(gè) LoadPath, 然后創(chuàng)建了一個(gè) InputStreamRewinder 類型的 DataRewinder, 最終將數(shù)據(jù)解析的操作到了 LoadPath.load 方法中
接下來看看這個(gè)LoadPath.load 做了哪些處理
public class LoadPath<Data, ResourceType, Transcode> {
public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
......
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
......
}
}
private final List<? extends DecodePath<Data, ResourceType, Transcode>> decodePaths;
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
@NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions) throws GlideException {
Resource<Transcode> result = null;
// 遍歷內(nèi)部存儲(chǔ)的 DecodePath 集合, 通過他們來解析數(shù)據(jù)
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
// 調(diào)用 DecodePath.decode 真正進(jìn)行數(shù)據(jù)的解析
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
......
}
......
}
return result;
}
}
public class DecodePath<DataType, ResourceType, Transcode> {
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
// 1. 調(diào)用 decodeResource 將源數(shù)據(jù)解析成中間資源
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
// 2. 調(diào)用 DecodeCallback.onResourceDecoded 處理中間資源
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
// 3. 調(diào)用 ResourceTranscoder.transcode 將中間資源轉(zhuǎn)為目標(biāo)資源
return transcoder.transcode(transformed, options);
}
可以看到 DecodePath.decode 操作一共有三種 resource
- 調(diào)用 decodeResource 將源數(shù)據(jù)解析成資源
- 即獲取 Bitmap 的過程
- 調(diào)用 DecodeCallback.onResourceDecoded 處理資源
- 對(duì) Bitmap 進(jìn)行 Transform 操作
- 調(diào)用 ResourceTranscoder.transcode 將資源轉(zhuǎn)為目標(biāo)資源
- 將 Bitmap 轉(zhuǎn)為目標(biāo)類型
我們先看看獲取 Bitmap 的操作
Step1 獲取 Bitmap
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options) throws GlideException {
try {
// 調(diào)用了 decodeResourceWithList
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
......
}
}
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
// 調(diào)用 ResourceDecoder.decode 解析源數(shù)據(jù)
result = decoder.decode(data, width, height, options);
}
}
......
if (result != null) {
break;
}
}
return result;
}
}
因?yàn)楸敬瘟鞒痰脑磾?shù)據(jù)為 InputStream 因此它的解析器為 StreamBitmapDecoder
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
private final Downsampler downsampler;
public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
@NonNull Options options)
throws IOException {
......
try {
// 根據(jù)請(qǐng)求配置的數(shù)據(jù), 對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 獲取到一個(gè) Resource<Bitmap>
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
......
}
}
}
可以看到它內(nèi)部通過 Downsampler.decode 方法對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 來獲取這個(gè)流的 Bitmap
- 這個(gè)采樣的策略就是我們?cè)跇?gòu)建 Request 時(shí)傳入的, 其采樣壓縮的細(xì)節(jié), 并不是我們本次關(guān)注的重點(diǎn)
我們看看獲取到了 Resource<Bitmap> 之后, 如何處理這個(gè)資源
Step2 Transform Bitmap
可以看到, 當(dāng)我們將源數(shù)據(jù)解析成對(duì)應(yīng)的資源之后, 便會(huì)調(diào)用 DecodeCallback.onResourceDecoded 處理資源, 我們看看它的處理過程
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {
@Override
public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
// 調(diào)用了外部類的 onResourceDecoded 方法
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
}
private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();
<Z> Resource<Z> onResourceDecoded(DataSource dataSource,
@NonNull Resource<Z> decoded) {
// 1. 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
......
// 2. 構(gòu)建磁盤緩存策略
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
......
final Key key;
switch (encodeStrategy) {
case SOURCE:
// 源 Bitmap 即 Source 緩存
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
// 變化后的 Bitmap 即 Resource 緩存
key =
new ResourceCacheKey(......);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
// 5. 初始化編碼管理者, 用于提交內(nèi)存緩存
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
// 返回 transform 之后的 bitmap
return result;
}
}
可以看到 onResourceDecoded 中, 主要是對(duì)中間資源做了如下的操作
-
調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
- 將資源轉(zhuǎn)為目標(biāo)效果, 如在構(gòu)建 request 時(shí), 設(shè)置的 CenterCrop
-
構(gòu)建磁盤緩存的 key
- 原始 Bitmap 即: Source 緩存
- Tranform 之后的為 Resource 緩存
好的, 這個(gè)方法執(zhí)行結(jié)束之后, 這個(gè)資源就與我們期望的效果一致了, 接下來只需要將它轉(zhuǎn)為目標(biāo)格式就可以展示了
Step3 將 Bitmap 轉(zhuǎn)為目標(biāo)類型
目標(biāo)數(shù)據(jù)為 Drawable, 因此它的轉(zhuǎn)換器為 BitmapDrawableTranscoder
public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
private final Resources resources;
@Nullable
@Override
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
// 調(diào)用了 LazyBitmapDrawableResource.obtain 獲取 Resource<BitmapDrawable> 的實(shí)例對(duì)象
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
}
public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
Initializable {
public static Resource<BitmapDrawable> obtain(
@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
......
// 創(chuàng)建了一個(gè) LazyBitmapDrawableResource
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
private LazyBitmapDrawableResource(@NonNull Resources resources,
@NonNull Resource<Bitmap> bitmapResource) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
}
public BitmapDrawable get() {
// Get 方法反回了一個(gè) BitmapDrawable 對(duì)象
return new BitmapDrawable(resources, bitmapResource.get());
}
}
好的, 轉(zhuǎn)化成目標(biāo)數(shù)據(jù)也非常的簡(jiǎn)單, 它將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內(nèi)部, 然后外界通過 get 方法就可以獲取到一個(gè) BitmapDrawable 的對(duì)象了
接下來看看 notifyEncodeAndRelease 如何展示數(shù)據(jù)的
2) 資源的緩存與展示
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
......
// 1. 回調(diào)上層資源準(zhǔn)備好了
notifyComplete(result, dataSource);
......
try {
// 2. 將數(shù)據(jù)緩存到磁盤
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
...
}
}
private Callback<R> callback;
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
......
// 1.1 從 DecodeJob 的構(gòu)建中, 我們知道這個(gè) Callback 是一 EngineJob
callback.onResourceReady(resource, dataSource);
}
}
好的, 可以看到 DecodeJob.decodeFromRetrievedData 中, 主要做了兩個(gè)操作
- 回調(diào) EngineJob.onResourceReady 資源準(zhǔn)備好了
- 將數(shù)據(jù)緩存到磁盤
這里我們主要看看 EngineJob.onResourceReady 中做了哪些處理
class EngineJob<R> implements DecodeJob.Callback<R>,
Poolable {
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
......
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
// 1. 通知上層 Engine 任務(wù)完成了
listener.onEngineJobComplete(this, localKey, localResource);
// 2. 回調(diào)給 ImageViewTarget 展示資源
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
}
}
EngineJob 中也是有兩步操作
- 一個(gè)是通知上層任務(wù)完成了
- 另一個(gè)是回調(diào)給 ImageViewTarget 展示資源
Step1 回調(diào) Engine 任務(wù)完成
我們先看看上層做了什么處理
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null) {
// 將加載好的資源添加到內(nèi)存緩存
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
......
}
}
我們知道在請(qǐng)求發(fā)起前是 Engine 嘗試通過內(nèi)存緩存讀, 結(jié)束之后再回到 Engine 添加內(nèi)存緩存也不足為奇了
Step2 通知 ImageViewTarget 展示資源
接下來我們看看 ImageViewTarget 展示資源的過程
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
// 處理一些 transition 變化, 在構(gòu)建 Request 時(shí)有分析過, 這里不贅述其實(shí)現(xiàn)細(xì)節(jié)了
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
......
}
}
private void setResourceInternal(@Nullable Z resource) {
// 調(diào)用了 setResource
setResource(resource);
......
}
}
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
protected void setResource(@Nullable Drawable resource) {
// 呈現(xiàn)到 View 上
view.setImageDrawable(resource);
}
}
ImageViewTarget 調(diào)用了子類重寫的 setResource 方法, 將數(shù)據(jù)填充進(jìn)去, 至此一次 Glide 圖像加載就完成了
四) 流程回顧
走到這里, 一個(gè)請(qǐng)求的數(shù)據(jù)源獲取就已經(jīng)完成, 其流程如下
- 任務(wù)的構(gòu)建
- 構(gòu)建這個(gè)請(qǐng)求的 key
-
從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
- 從 ActiveResources 緩存中查找
- 從 LruResourceCache 緩存中查找
-
從緩存中查找 key 對(duì)應(yīng)的任務(wù)
- 若存在則說明無需再次獲取資源
- 構(gòu)建引擎任務(wù) EngineJob
- 引擎的任務(wù)為解碼任務(wù) DecodeJob
- 將任務(wù)添加到緩存, 防止多次構(gòu)建
- 執(zhí)行 EngineJob
- 任務(wù)的執(zhí)行
- 通過 getNextStage 獲取 Decode 場(chǎng)景
- 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
- 調(diào)用 runGenerators 處理任務(wù)
- 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
- 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
- 最終方案重新獲取 Source 數(shù)據(jù)
- Source 通過 HttpUrlFetcher 獲取網(wǎng)絡(luò)數(shù)據(jù)流 InputStream
- DecodeJob 的 onDataFetcherReady 處理 InputStream
- 解析 InputStream
- 通過 Downsampler 將 InputStream 解析成 Bitmap
- 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
- 將 Bitmap 轉(zhuǎn)為 Drawable
- 資源的緩存與展示
- Engine 進(jìn)行內(nèi)存緩存
- ViewTarget 展示資源
- DecodeJob 進(jìn)行磁盤緩存
- 原始 Bitmap 對(duì)應(yīng) SOURCE 類型
- Transform 之后的 Bitmap 對(duì)應(yīng) RESOURCE 類型
- 解析 InputStream
總結(jié)
通過一次流程分析我們得知, 整個(gè) Glide 圖片加載主要有如下幾步
- with
- 獲取全局單例的 Glide 對(duì)象
- 獲取全局單例的 RequestManagerRetriever
- 創(chuàng)建當(dāng)前 Context 對(duì)應(yīng)的 RequestManager
- load
- 根據(jù)的寬高览芳、采樣的方式篱蝇、transform 變化等構(gòu)建一個(gè) RequestBuilder 對(duì)象
- into
- 根據(jù) ImageView 構(gòu)建采樣壓縮和圖像變化的策略保存在 Options 和 Transform 中
- 構(gòu)建 ViewTarget, 描述這個(gè)請(qǐng)求要作用的 View 對(duì)象
- 調(diào)用 into 重載方法
- 構(gòu)建 Request 對(duì)象
- 為 ViewTarget 綁定 Request
- 交由 RequestManger 執(zhí)行該請(qǐng)求
- 請(qǐng)求的執(zhí)行
- 任務(wù)的構(gòu)建
- 構(gòu)建這個(gè)請(qǐng)求的 key
-
從內(nèi)存緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
- 從 ActiveResources 緩存中查找
- 從 LruResourceCache 緩存中查找
-
從緩存中查找 key 對(duì)應(yīng)的任務(wù)
- 若存在則說明無需再次獲取資源
- 構(gòu)建引擎任務(wù) EngineJob
- 引擎的任務(wù)為解碼任務(wù) DecodeJob
- 將任務(wù)添加到緩存, 防止多次構(gòu)建
- 執(zhí)行 EngineJob
- 任務(wù)的執(zhí)行
- 通過 getNextStage 獲取 Decode 場(chǎng)景
- 構(gòu)建當(dāng)前場(chǎng)景的處理器 DataFetcherGenerator
- 調(diào)用 runGenerators 處理任務(wù)
- 優(yōu)先從 Resource 的磁盤緩存中取數(shù)據(jù)
- 次優(yōu)先從 Data 的磁盤緩存中取數(shù)據(jù)
- 最終方案重新獲取 Source 數(shù)據(jù)
- Source 通過 HttpUrlFetcher 獲取網(wǎng)絡(luò)數(shù)據(jù)流 InputStream
- DecodeJob 的 onDataFetcherReady 處理 InputStream
- 解析 InputStream
- 通過 Downsampler 將 InputStream 解析成 Bitmap
- 調(diào)用 Transformation.transformation 對(duì) Bitmap 進(jìn)行變化操作
- 將 Bitmap 轉(zhuǎn)為 BitmapDrawable
- 資源的緩存與展示
- Engine 進(jìn)行內(nèi)存緩存
- ViewTarget 展示資源
- DecodeJob 進(jìn)行磁盤緩存
- 原始 Bitmap 對(duì)應(yīng) SOURCE 類型
- Transform 之后的 Bitmap 對(duì)應(yīng) RESOURCE 類型
- 任務(wù)的構(gòu)建
看了 Glide 的加載流程, 我似乎能夠明白為什么他是 Google 推薦的圖片加載框架了, 內(nèi)部細(xì)節(jié)的處理做的非常的到位, 而且使用 GlideContext 用于描述 Glide 的上下文, 與 Android 的 Context 巧妙的融合在一起, 讀起來真有一種閱讀 Android 源碼的既視感
不過這只是最簡(jiǎn)單的流程, 而且 Glide 支持 Gif, 視頻加載操作, 可想而知其內(nèi)部的 Decorder 處理了多少邏輯代碼, 如此復(fù)雜的流程, 嵌套了如此之多的回調(diào), 無疑增加了我們閱讀源碼的難度, 若是將這些操作分層, 并且使用攔截器去實(shí)現(xiàn), 我想定會(huì)讓一次圖像加載操作變得更加清晰明了