最近有個想法——就是把 Android 主流開源框架進行深刻分析,而後寫成一系列文章,包括該框架的詳細使用與源碼解析。目的是經過鑑賞大神的源碼來了解框架底層的原理,也就是作到不只要知其然,還要知其因此然。html
這裏我說下本身閱讀源碼的經驗,我通常都是按照平時使用某個框架或者某個系統源碼的使用流程入手的,首先要知道怎麼使用,而後再去深究每一步底層作了什麼,用了哪些好的設計模式,爲何要這麼設計。java
系列文章:android
更多幹貨請關注 AndroidNotesgit
Glide 是一個快速高效的 Android 圖片加載庫,也是 Google 官方推薦的圖片加載庫。多數狀況下,使用 Glide 加載圖片很是簡單,一行代碼就能解決。以下:github
Glide.with(this).load(url).into(imageView);
複製代碼
這行代碼用起來雖然簡單,可是涉及到的三個方法 with()、load()、into() 的內部實現是比較複雜的,接下來咱們就根據這三個方法進行源碼閱讀。這裏沒有單獨用一篇文章來寫 Glide 的使用,是由於官方文檔已經很是詳細了,看不懂英文的能夠直接看中文的。設計模式
這篇文章主要分析 Glide 的執行流程,Glide 的緩存機制在下一篇文章中分析,這兩篇都是使用最新的 4.11.0 版原本分析。api
由於 Glide 默認是配置了內存與磁盤緩存的,因此這裏咱們先禁用內存和磁盤緩存。以下設置:數組
Glide.with(this)
.load(url)
.skipMemoryCache(true) // 禁用內存緩存
.diskCacheStrategy(DiskCacheStrategy.NONE) // 禁用磁盤緩存
.into(imageView);
複製代碼
注意:後面的分析都是加了跳過緩存的,因此你在跟着本文分析的時候記得加上上面兩句。緩存
with() 的重載方法有 6 個:網絡
這些方法的參數能夠分紅兩種狀況,即 Application(Context)類型與非 Application(Activity、Fragment、View,這裏的 View 獲取的是它所屬的 Activity 或 Fragment)類型,這些參數的做用是肯定圖片加載的生命週期。點擊進去發現 6 個重載方法最終都會調用 getRetriever().get(),因此這裏只拿 FragmentActivity 來演示:
/*Glide*/
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
複製代碼
點擊 getRetriever() 方法進去:
/*Glide*/
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
...
return Glide.get(context).getRequestManagerRetriever();
}
複製代碼
繼續看下 Glide#get(context):
/*Glide*/
public static Glide get(@NonNull Context context) {
if (glide == null) {
//(1)
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
//(2)
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
複製代碼
這裏使用了雙重校驗鎖的單例模式來獲取 Glide 的實例,其中關注點(1)點擊進去看看:
/*Glide*/
private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
GeneratedAppGlideModule result = null;
Class<GeneratedAppGlideModule> clazz =
(Class<GeneratedAppGlideModule>)
Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
result =
clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
...
return result;
}
複製代碼
該方法用來實例化咱們用 @GlideModule 註解標識的自定義模塊,這裏提一下自定義模塊的用法。
public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
複製代碼
其中 applyOptions() 與 registerComponents() 方法分別用來更改 Glide 的配置以及替換 Glide 組件。
而後在 AndroidManifest.xml 文件中加入以下配置:
<application>
...
<meta-data
android:name="com.wildma.myapplication.MyGlideModule"
android:value="GlideModule" />
</application>
複製代碼
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
}
}
複製代碼
關於 @GlideModule 的更詳細使用能夠查看 Generated API。
接下來繼續查看關注點(2):
/*Glide*/
private static void checkAndInitializeGlide( @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// 不能重複初始化
if (isInitializing) {
throw new IllegalStateException(
"You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
initializeGlide(context, generatedAppGlideModule);
isInitializing = false;
}
/*Glide*/
private static void initializeGlide( @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
/*Glide*/
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();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
//(1)
manifestModules = new ManifestParser(applicationContext).parse();
}
...
// 從註解生成的 GeneratedAppGlideModule 中獲取 RequestManagerFactory
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
// 將 RequestManagerFactory 設置到 GlideBuilder
builder.setRequestManagerFactory(factory);
//(2)
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
//(3)
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
//(4)
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
try {
// (5)
module.registerComponents(applicationContext, glide, glide.registry);
} catch (AbstractMethodError e) {
throw new IllegalStateException(
"Attempting to register a Glide v3 module. If you see this, you or one of your"
+ " dependencies may be including Glide v3 even though you're using Glide v4."
+ " You'll need to find and remove (or update) the offending dependency."
+ " The v3 module name is: "
+ module.getClass().getName(),
e);
}
}
if (annotationGeneratedModule != null) {
// (6)
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
// 註冊組件回調
applicationContext.registerComponentCallbacks(glide);
//(7)
Glide.glide = glide;
}
複製代碼
能夠看到,調用 checkAndInitializeGlide() 方法後最終調用了最多參數的 initializeGlide() 方法。源碼中我標記了 7 個關注點,分別以下:
(1):將 AndroidManifest.xml 中全部值爲 GlideModule 的 meta-data 配置讀取出來,並將相應的自定義模塊實例化。也就是實例化前面演示的在 Glide 3 中的自定義模塊。
(2)(3):分別從兩個版本的自定義模塊中更改 Glide 的配置。
(4):使用建造者模式建立 Glide。
(5)(6):分別從兩個版本的自定義模塊中替換 Glide 組件。
(7):將建立的 Glide 賦值給 Glide 的靜態變量。
繼續看下關注點(4)內部是如何建立 Glide 的,進入 GlideBuilder#build():
/*GlideBuilder*/
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
// 建立網絡請求線程池
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
// 建立磁盤緩存線程池
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
// 建立動畫線程池
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
// 建立內存大小計算器
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
// 建立默認網絡鏈接監視器工廠
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
// 建立 Bitmap 池
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
// 建立固定大小的數組池(4MB),使用 LRU 策略來保持數組池在最大字節數如下
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
// 建立內存緩存
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {、
// 建立磁盤緩存工廠
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
/*建立加載以及管理活動資源和緩存資源的引擎*/
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
// 建立請求管理類,這裏的 requestManagerFactory 就是前面 GlideBuilder#setRequestManagerFactory() 設置進來的
// 也就是 @GlideModule 註解中獲取的
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
//(1)建立 Glide
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps);
}
複製代碼
能夠看到,build() 方法主要是建立一些線程池、Bitmap 池、緩存策略、Engine 等,而後利用這些來建立具體的 Glide。繼續看下 Glide 的構造函數:
/*Glide*/
Glide(...) {
/*將傳進來的參數賦值給 Glide 類中的一些常量,方便後續使用。*/
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;
final Resources resources = context.getResources();
// 建立 Registry,Registry 的做用是管理組件註冊,用來擴展或替換 Glide 的默認加載、解碼和編碼邏輯。
registry = new Registry();
// 省略的部分主要是:建立一些處理圖片的解析器、解碼器、轉碼器等,而後將他們添加到 Registry 中
...
// 建立 ImageViewTargetFactory,用來給 View 獲取正確類型的 ViewTarget(BitmapImageViewTarget 或 DrawableImageViewTarget)
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
// 構建一個 Glide 專屬的上下文
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
複製代碼
到這一步,Glide 纔算真正建立成功。也就是 Glide.get(context).getRequestManagerRetriever() 中的 get() 方法已經走完了。
接下來看下 getRequestManagerRetriever() 方法:
/*Glide*/
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
複製代碼
發現這裏直接返回了實例,其實 RequestManagerRetriever 的實例在前面的 GlideBuilder#build() 方法中已經建立了,因此這裏能夠直接返回。
到這一步,getRetriever(activity).get(activity) 中的 getRetriever() 方法也已經走完了,並返回了 RequestManagerRetriever,接下來繼續看下 get() 方法。
RequestManagerRetriever#get() 的重載方法一樣有 6 個:
這些方法的參數一樣能夠分紅兩種狀況,即 Application 與非 Application 類型。先看下 Application 類型的狀況:
/*RequestManagerRetriever*/
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)*/
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
//(2)
return getApplicationManager(context);
}
複製代碼
這裏標註了 2 個關注點,分別以下:
/*RequestManagerRetriever*/
private RequestManager getApplicationManager(@NonNull Context context) {
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;
}
複製代碼
能夠看到,這裏使用了單例模式來獲取 RequestManager,裏面從新用 Application 類型的 Context 來獲取 Glide 的實例。這裏並無專門作生命週期的處理, 由於 Application 對象的生命週期即爲應用程序的生命週期,因此在這裏圖片請求的生命週期是和應用程序同步的。
咱們繼續看下非 Application 類型的狀況,這裏只拿 FragmentActivity 來說,其餘相似。代碼以下:
/*RequestManagerRetriever*/
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
// 檢查 Activity 是否銷燬
assertNotDestroyed(activity);
// 獲取當前 Activity 的 FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
複製代碼
這裏判斷若是是後臺線程那麼仍是走上面的 Application 類型的 get() 方法,不然調用 supportFragmentGet() 方法來獲取 RequestManager。 看下 supportFragmentGet() 方法:
/*RequestManagerRetriever*/
private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//(1)
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
//(2)
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
//(3)
current.setRequestManager(requestManager);
}
return requestManager;
}
/*RequestManagerRetriever*/
// 關注點(1)調用了 getSupportRequestManagerFragment() 方法
private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//(4)
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
/*SupportRequestManagerFragment*/
// 關注點(4)實例化了 SupportRequestManagerFragment
public SupportRequestManagerFragment() {
//(5)
this(new ActivityFragmentLifecycle());
}
複製代碼
在 supportFragmentGet() 方法中我標註了 3 個關注點,分別以下:
因此通過(2)(3)實際是將 SupportRequestManagerFragment、RequestManager、ActivityFragmentLifecycle 都關聯在一塊兒了,又由於 Fragment 的生命週期和 Activity 是同步的, 因此 Activity 生命週期發生變化的時候,隱藏的 Fragment 的生命週期是同步變化的,這樣 Glide 就能夠根據這個 Fragment 的生命週期進行請求管理了。
是否是這樣的呢?咱們去看看 SupportRequestManagerFragment 的生命週期就知道了。
/*SupportRequestManagerFragment*/
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
複製代碼
能夠看到,ActivityFragmentLifecycle 確實與 SupportRequestManagerFragment 的生命週期關聯起來了,咱們這裏只拿 onDestroy 來看看, 這裏調用了 lifecycle#onDestroy(),間接調用了以下方法:
/*ActivityFragmentLifecycle*/
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
/*LifecycleListener*/
void onDestroy();
/*RequestManager*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
複製代碼
能夠看到,由於 RequestManager 是實現了 LifecycleListener 接口的,因此最終是調用了 RequestManager 的 onDestroy() 方法, 該方法裏面主要作了取消全部進行中的請求並清除和回收全部已完成請求的資源。確實如咱們所料,圖片請求的生命週期就是由一個隱藏的 Fragment 的生命週期決定的。
這裏總結下,RequestManagerRetriever#get() 方法若是傳入 Application 類型的參數,那麼圖片請求的生命週期就是 Application 對象的生命週期,即應用程序的生命週期。 若是傳入的是非 Application 類型的參數,那麼會向當前的 Activity 當中添加一個隱藏的 Fragment,而後圖片請求的生命週期由這個 Fragment 的生命週期決定。
with() 方法主要作了以下事情:
load() 的重載方法有 9 個:
點擊進去發現這些重載方法最終都會調用 asDrawable().load(),因此這裏只拿參數爲字符串的來演示,也就是參數爲圖片連接。以下:
/*RequestManager*/
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
複製代碼
點擊 asDrawable() 方法進去看看:
/*RequestManager*/
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
/*RequestManager*/
public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
/*RequestBuilder*/
protected RequestBuilder( @NonNull Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) {
/*給 RequestBuilder 中的一些常量進行賦值*/
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
// 初始化請求監聽
initRequestListeners(requestManager.getDefaultRequestListeners());
//(1)
apply(requestManager.getDefaultRequestOptions());
}
複製代碼
能夠看到,通過一些列調用,該方法最終建立了 RequestBuilder 的實例並返回。其中關注點(1)是將默認選項應用於請求,點進去看看裏面作了什麼:
/*RequestBuilder*/
@Override
public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
Preconditions.checkNotNull(requestOptions);
return super.apply(requestOptions);
}
複製代碼
這裏又調用了父類的 apply() 方法:
/*BaseRequestOptions*/
public T apply(@NonNull BaseRequestOptions<?> o) {
if (isAutoCloneEnabled) {
return clone().apply(o);
}
BaseRequestOptions<?> other = o;
if (isSet(other.fields, SIZE_MULTIPLIER)) {
sizeMultiplier = other.sizeMultiplier;
}
if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {
useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;
}
if (isSet(other.fields, USE_ANIMATION_POOL)) {
useAnimationPool = other.useAnimationPool;
}
if (isSet(other.fields, DISK_CACHE_STRATEGY)) {
diskCacheStrategy = other.diskCacheStrategy;
}
if (isSet(other.fields, PRIORITY)) {
priority = other.priority;
}
if (isSet(other.fields, ERROR_PLACEHOLDER)) {
errorPlaceholder = other.errorPlaceholder;
errorId = 0;
fields &= ~ERROR_ID;
}
if (isSet(other.fields, ERROR_ID)) {
errorId = other.errorId;
errorPlaceholder = null;
fields &= ~ERROR_PLACEHOLDER;
}
if (isSet(other.fields, PLACEHOLDER)) {
placeholderDrawable = other.placeholderDrawable;
placeholderId = 0;
fields &= ~PLACEHOLDER_ID;
}
if (isSet(other.fields, PLACEHOLDER_ID)) {
placeholderId = other.placeholderId;
placeholderDrawable = null;
fields &= ~PLACEHOLDER;
}
if (isSet(other.fields, IS_CACHEABLE)) {
isCacheable = other.isCacheable;
}
if (isSet(other.fields, OVERRIDE)) {
overrideWidth = other.overrideWidth;
overrideHeight = other.overrideHeight;
}
if (isSet(other.fields, SIGNATURE)) {
signature = other.signature;
}
if (isSet(other.fields, RESOURCE_CLASS)) {
resourceClass = other.resourceClass;
}
if (isSet(other.fields, FALLBACK)) {
fallbackDrawable = other.fallbackDrawable;
fallbackId = 0;
fields &= ~FALLBACK_ID;
}
if (isSet(other.fields, FALLBACK_ID)) {
fallbackId = other.fallbackId;
fallbackDrawable = null;
fields &= ~FALLBACK;
}
if (isSet(other.fields, THEME)) {
theme = other.theme;
}
if (isSet(other.fields, TRANSFORMATION_ALLOWED)) {
isTransformationAllowed = other.isTransformationAllowed;
}
if (isSet(other.fields, TRANSFORMATION_REQUIRED)) {
isTransformationRequired = other.isTransformationRequired;
}
if (isSet(other.fields, TRANSFORMATION)) {
transformations.putAll(other.transformations);
isScaleOnlyOrNoTransform = other.isScaleOnlyOrNoTransform;
}
if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {
onlyRetrieveFromCache = other.onlyRetrieveFromCache;
}
// Applying options with dontTransform() is expected to clear our transformations.
if (!isTransformationAllowed) {
transformations.clear();
fields &= ~TRANSFORMATION;
isTransformationRequired = false;
fields &= ~TRANSFORMATION_REQUIRED;
isScaleOnlyOrNoTransform = true;
}
fields |= other.fields;
options.putAll(other.options);
return selfOrThrowIfLocked();
}
複製代碼
能夠看到,配置選項有不少,包括磁盤緩存策略,加載中的佔位圖,加載失敗的佔位圖等。 到這裏 asDrawable() 方法就看完了,接下來繼續看 asDrawable().load(string) 中的 load() 方法。
點擊 load() 方法進去看看:
/*RequestBuilder*/
@Override
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
/*RequestBuilder*/
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
複製代碼
發現這個方法很是簡單,首先調用了 loadGeneric() 方法,而後 loadGeneric() 方法中將傳進來的圖片資源賦值給了變量 model,最後用 isModelSet 標記已經調用過 load() 方法了。
load() 方法就比較簡單了,主要是經過前面實例化的 Glide 與 RequestManager 來建立 RequestBuilder,而後將傳進來的參數賦值給 model。接下來主要看看 into() 方法。
咱們發現,前兩個方法都沒有涉及到圖片的請求、緩存、解碼等邏輯,其實都在 into() 方法中,因此這個方法也是最複雜的。
點擊 into() 方法進去看看:
/*RequestBuilder*/
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
/*將 ImageView 的 scaleType 設置給 BaseRequestOptions(ImageView 的默認 scaleType 爲 fitCenter)*/
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.
}
}
//(1)buildImageViewTarget()
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
/*RequestBuilder*/
private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//(2)
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
//(3)
requestManager.track(target, request);
return target;
}
複製代碼
能夠看到,裏面又調用了 into() 的另外一個重載方法。這裏我標記了 3 個關注點,分別是獲取 ImageViewTarget、構建請求和執行請求,後面咱們就主要分析這 3 個關注點。
點擊 **RequestBuilder#into() 中的關注點(1)**進去看看:
/*GlideContext*/
public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
/*ImageViewTargetFactory*/
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)");
}
}
複製代碼
能夠看到,這裏是經過 ImageViewTargetFactory#buildTarget(imageView, transcodeClass) 來獲取的,其中 ImageViewTargetFactory 是在 Glide 的構造函數中實例化的,而 transcodeClass 是 load() 方法中 asDrawable()--as(Drawable.class) 傳進來的。而後 buildTarget() 方法中就是根據這個 transcodeClass 來返回對應的 ViewTarget,因此默認狀況下都是返回 DrawableImageViewTarget,只有專門指定 asBitmap() 纔會返回 BitmapImageViewTarget,以下指定:
Glide.with(this).asBitmap().load(url).into(imageView);
複製代碼
接下來繼續看關注點(2)。
點擊 **RequestBuilder#into() 中的關注點(2)**進去看看:
/*RequestBuilder*/
private Request buildRequest(...) {
return buildRequestRecursive(
/*requestLock=*/ new Object(),
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
複製代碼
這裏又調用了 buildRequestRecursive() 方法:
/*RequestBuilder*/
private Request buildRequestRecursive(...) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
//(1)
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
//(2)遞歸構建縮略圖請求
Request mainRequest =
buildThumbnailRequestRecursive(...);
if (errorRequestCoordinator == null) {
return mainRequest;
}
...
//(3)遞歸構建錯誤請求
Request errorRequest =
errorBuilder.buildRequestRecursive(...);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
複製代碼
能夠看到,只有關注點(1)中的 errorBuilder 不爲空,即咱們設置了在主請求失敗時開始新的請求(以下設置) 纔會走到關注點(3)去遞歸構建錯誤請求。
// 設置在主請求失敗時開始新的請求
Glide.with(this).load(url).error(Glide.with(this).load(fallbackUrl)).into(imageView);
複製代碼
由於我什麼都沒有設置,因此這裏看關注點(2)的遞歸構建縮略圖請求便可。
點擊 buildThumbnailRequestRecursive() 方法進去看看:
/*RequestBuilder*/
private Request buildThumbnailRequestRecursive( Object requestLock, Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) {
if (thumbnailBuilder != null) { //(1)
...
//(1.1)
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
//(1.2)
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
//(1.3)
Request thumbRequest =
thumbnailBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
coordinator,
thumbTransitionOptions,
thumbPriority,
thumbOverrideWidth,
thumbOverrideHeight,
thumbnailBuilder,
callbackExecutor);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) { //(2)
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
BaseRequestOptions<?> thumbnailOptions =
requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest =
obtainRequest(
requestLock,
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight,
callbackExecutor);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else { //(3)
// Base case: no thumbnail.
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
複製代碼
能夠看到,這裏我標記了 3 個大的關注點,分別以下:
// 設置縮略圖請求
Glide.with(this).load(url).thumbnail(Glide.with(this).load(thumbnailUrl)).into(imageView);
複製代碼
其中關注點(1.2)是獲取一個原圖請求,關注點(1.3)是根據設置的 thumbnailBuilder 來生成縮略圖請求,而後關注點(1.1)是建立一個協調器,用來協調這兩個請求,這樣能夠同時進行原圖與縮略圖的請求。
// 設置縮略圖的縮略比例
Glide.with(this).load(url).thumbnail(0.5f).into(imageView);
複製代碼
與關注點(1)同樣,也是經過一個協調器來同時進行原圖與縮略圖的請求,不一樣的是這裏生成縮略圖用的是縮略比例。
由於我什麼都沒有設置,因此這裏看關注點(3)便可。點擊 obtainRequest() 方法進去看看:
/*RequestBuilder*/
private Request obtainRequest(...) {
return SingleRequest.obtain(...);
}
/*SingleRequest*/
public static <R> SingleRequest<R> obtain(...) {
return new SingleRequest<>(...);
}
/*SingleRequest*/
private SingleRequest( Context context, GlideContext glideContext, @NonNull Object requestLock, @Nullable Object model, Class<R> transcodeClass, BaseRequestOptions<?> requestOptions, int overrideWidth, int overrideHeight, Priority priority, Target<R> target, @Nullable RequestListener<R> targetListener, @Nullable List<RequestListener<R>> requestListeners, RequestCoordinator requestCoordinator, Engine engine, TransitionFactory<? super R> animationFactory, Executor callbackExecutor) {
this.requestLock = requestLock;
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
this.requestOptions = requestOptions;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.priority = priority;
this.target = target;
this.targetListener = targetListener;
this.requestListeners = requestListeners;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
this.callbackExecutor = callbackExecutor;
status = Status.PENDING;
if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
requestOrigin = new RuntimeException("Glide request origin trace");
}
}
複製代碼
能夠看到,最終是實例化了一個 SingleRequest 的實例,也就是說構建請求這一步已經完成了,接下來分析執行請求這一步。
點擊 **RequestBuilder#into() 中的關注點(3)**進去看看:
/*RequestManager*/
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
複製代碼
點擊 track() 進去:
public final class TargetTracker implements LifecycleListener {
private final Set<Target<?>> targets =
Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
}
複製代碼
能夠看到,targetTracker.track(target) 是將一個 target 加入 Set 集合中,繼續看下 runRequest() 方法:
/*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) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
複製代碼
能夠看到,首先將請求加入一個 Set 集合,而後判斷是否暫停狀態,是則清空請求,並將該請求加入待處理的請求集合中;不是暫停狀態則開始請求。
點擊 begin() 方法進去看看:
/*SingleRequest*/
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
/*若是加載的資源爲空,則調用加載失敗的回調*/
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
// 資源加載完成
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
//(1)
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 剛開始加載的回調
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
}
複製代碼
如上,關鍵代碼在關注點(1),這裏作了一個判斷,若是咱們設置了 overrideWidth 和 overrideHeight(以下設置),則直接調用 onSizeReady() 方法,不然調用 getSize() 方法(第一次加載纔會調用)。
// 設置加載圖片的寬高爲 100x100 px
Glide.with(this).load(url).override(100,100).into(imageView);
複製代碼
getSize() 方法的做用是經過 ViewTreeObserver 來監聽 ImageView 的寬高,拿到寬高後最終也是調用 onSizeReady() 方法。
接下來看下 onSizeReady() 方法:
/*SingleRequest*/
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
...
/*根據縮略比例獲取圖片寬高*/
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
// 開始加載
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
...
}
}
複製代碼
能夠看到,這裏開始加載了,繼續跟進:
/*Engine*/
public <R> LoadStatus load( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 構建緩存 key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
// 從內存中加載資源
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
// 內存中沒有,則等待當前正在執行的或開始一個新的 EngineJob
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// 加載完成回調
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
複製代碼
能夠看到,這裏首先構建緩存 key,而後利用這個 key 獲取緩存中的資源,若是內存中沒有,則等待當前正在執行的或開始一個新的 EngineJob,內存中有則調用加載完成的回調。這裏咱們先不討論緩存相關的,下一篇文章再講。因此默認是第一次加載,點擊 waitForExistingOrStartNewJob() 方法進去看看:
/*Engine*/
private <R> LoadStatus waitForExistingOrStartNewJob( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor, EngineKey key, long startTime) {
//(1)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//(2)
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//(3)
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
// 將 EngineJob 放到一個 map 集合中
jobs.put(key, engineJob);
// 添加回調
engineJob.addCallback(cb, callbackExecutor);
//(4)
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
// 返回加載狀態
return new LoadStatus(cb, engineJob);
}
複製代碼
這裏標記了 4 個關注點,分別以下:
/*EngineJob*/
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
複製代碼
這裏直接將 DecodeJob 任務放到了一個線程池中去執行,也就是說從這裏開始切換到子線程了,那麼咱們看下 DecodeJob 的 run() 方法作了什麼:
/*DecodeJob*/
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
// 被取消,則調用加載失敗的回調
if (isCancelled) {
notifyFailed();
return;
}
// 執行
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
...
} finally {
// Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
// close in all cases anyway.
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
複製代碼
繼續 runWrapped() 方法:
/*DecodeJob*/
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
/*(4.1)*/
// 獲取資源狀態
stage = getNextStage(Stage.INITIALIZE);
// 根據資源狀態獲取資源執行器
currentGenerator = getNextGenerator();
//(4.2)執行
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
/*DecodeJob*/
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
/*DecodeJob*/
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
複製代碼
runReason 的默認值爲 INITIALIZE,因此走的是第一個 case。因爲咱們配置了禁用內存與磁盤緩存,因此關注點(4.1)中獲得的 stage 爲 SOURCE,currentGenerator 爲 SourceGenerator。拿到資源執行器接着就是執行了,點擊關注點(4.2)的 runGenerators() 方法進去看看:
/*DecodeJob*/
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
//(1)startNext()
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
}
複製代碼
由於 currentGenerator 在這裏爲 SourceGenerator,因此調用 startNext() 方法的時候實際調用的是 SourceGenerator#startNext()。該方法執行完返回的結果爲 true,因此 while 循環是進不去的。
那麼咱們接下來看下 SourceGenerator#startNext():
/*SourceGenerator*/
@Override
public boolean startNext() {
...
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//(1)
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//(2)
startNextLoad(loadData);
}
}
return started;
}
複製代碼
這裏我標記了 2 個關注,分別以下:
/*DecodeHelper*/
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
// 關注點
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
複製代碼
能夠看到,這裏是經過 ModelLoader 對象的 buildLoadData() 方法獲取的 LoadData。因爲是從網絡加載數據,因此這裏實際調用的是 HttpGlideUrlLoader#buildLoadData(),點進去看看:
/*HttpGlideUrlLoader*/
@Override
public LoadData<InputStream> buildLoadData( @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
// 關注點
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
複製代碼
緩存相關的不看,看到最後一行實例化 LoadData 的時候順帶實例化了 HttpUrlFetcher,這個後面會用到。
/*SourceGenerator*/
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
複製代碼
這裏的 loadData.fetcher 就是剛剛實例化 LoadData 的時候傳進來的 HttpUrlFetcher,因此這裏調用的是 HttpUrlFetcher#loadData():
/*HttpUrlFetcher*/
@Override
public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//(1)經過重定向加載數據
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//(2)加載成功,回調數據
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
複製代碼
這裏標記了 2 個關注點,分別以下:
/*HttpUrlFetcher*/
private InputStream loadDataWithRedirects( URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
...
// 獲取 HttpURLConnection 實例
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) { // 請求成功
// 獲取 InputStream
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) { // 重定向請求
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
/*HttpUrlFetcher*/
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
複製代碼
能夠看到,原來 Glide 底層是採用 HttpURLConnection 來進行網絡請求的,請求成功後返回了 InputStream。
/*MultiModelLoader*/
@Override
public void onDataReady(@Nullable Data data) {
if (data != null) {
// 執行這裏
callback.onDataReady(data);
} else {
startNextOrFail();
}
}
/*SourceGenerator*/
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
// 執行這裏
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
/*SourceGenerator*/
void onDataReadyInternal(LoadData<?> loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
// 執行這裏
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
/*DecodeJob*/
@Override
public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// (1)解碼
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
複製代碼
能夠看到,通過一系列調用,最終走到關注點(1),這裏就開始解碼數據了,點進去看看:
/*DecodeJob*/
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
//(1)解碼
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//(2)解碼完成,通知下去
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
複製代碼
這裏標記了 2 個關注點,分別以下:
/*DecodeJob*/
private <Data> Resource<R> decodeFromData( DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
/*DecodeJob*/
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException {
// 獲取解碼器,解碼器裏面封裝了 DecodePath,它就是用來解碼轉碼的
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 經過解碼器解析數據
return runLoadPath(data, dataSource, path);
}
/*DecodeJob*/
private <Data, ResourceType> Resource<R> runLoadPath( Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
// 將解碼任務傳遞給 LoadPath 完成
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
/*LoadPath*/
public Resource<Transcode> load( DataRewinder<Data> rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
/*LoadPath*/
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;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
// 開始解析數據
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
複製代碼
能夠看到,上面主要是獲取解碼器(LoadPath),而後解碼器裏面是封裝了 DecodePath,通過一系列調用,最後實際上是交給 DecodePath 的 decode() 方法來真正開始解析數據的。
點擊 decode() 方法進去看看:
/*DecodePath*/
public Resource<Transcode> decode( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
//(1)
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
//(2)
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//(3)
return transcoder.transcode(transformed, options);
}
複製代碼
這裏我標記了 3 個關注點,分別以下:
/*DecodePath*/
private Resource<ResourceType> decodeResource( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options) throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
/*DecodePath*/
private Resource<ResourceType> decodeResourceWithList( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
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();
// 關注點
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
...
}
...
return result;
}
複製代碼
能夠看到,這裏遍歷拿到能夠解碼當前數據的資源解碼器,而後調用 decode() 方法進行解碼。由於當前數據是 InputStream,因此這裏遍歷拿到的 ResourceDecoder 實際上是 StreamBitmapDecoder,因此調用的是 StreamBitmapDecoder#decode()。
繼續看 StreamBitmapDecoder#decode():
/*StreamBitmapDecoder*/
@Override
public Resource<Bitmap> decode( @NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a
// way to determine if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still
// attempt to decode the full image even when the header decode fails and/or overflows our read
// buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
// (1)
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
/*Downsampler*/
public Resource<Bitmap> decode(...) throws IOException {
return decode(...);
}
/*Downsampler*/
private Resource<Bitmap> decode(...) throws IOException {
...
try {
// (2)根據輸入流解碼獲得 Bitmap
Bitmap result =
decodeFromWrappedStreams(
imageReader,
bitmapFactoryOptions,
downsampleStrategy,
decodeFormat,
preferredColorSpace,
isHardwareConfigAllowed,
requestedWidth,
requestedHeight,
fixBitmapToRequestedDimensions,
callbacks);
// (3)將 Bitmap 包裝成 Resource<Bitmap> 返回
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
複製代碼
能夠看到,關注點(1)中調用了 Downsampler#decode(),而後走到關注點(2)將輸入流解碼獲得 Bitmap,最後將 Bitmap 包裝成 Resource 返回。關注點(2)中其實就是使用 BitmapFactory 根據 exif 方向對圖像進行下采樣,解碼和旋轉,最後調用原生的 BitmapFactory#decodeStream() 獲得的 Bitmap,裏面的細節就不展開了。
/*DecodeJob*/
@Override
public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
複製代碼
繼續跟進:
/*DecodeJob*/
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
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);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
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;
// 緩存相關
...
return result;
}
複製代碼
能夠看到,這裏的任務是對資源進行轉換,也就是咱們請求的時候若是配置了 centerCrop、fitCenter 等,這裏就須要轉換成對應的資源。
/*BitmapDrawableTranscoder*/
@Override
public Resource<BitmapDrawable> transcode( @NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
複製代碼
繼續往下跟:
/*LazyBitmapDrawableResource*/
public static Resource<BitmapDrawable> obtain( @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
/*LazyBitmapDrawableResource*/
private LazyBitmapDrawableResource( @NonNull Resources resources, @NonNull Resource<Bitmap> bitmapResource) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
}
複製代碼
由於 LazyBitmapDrawableResource 實現了 Resource,因此最後是返回了一個 Resource,也就是說轉碼這一步是將 Resource 轉成了 Resource。
到這裏,DecodeJob#decodeFromRetrievedData() 中的關注點(1)解碼的流程就走完了,咱們回去繼續看關注點(2)。
/*DecodeJob*/
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
//(1)解碼
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//(2)解碼完成,通知下去
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
複製代碼
剛剛關注點(1)的解碼過程已經完成,如今須要通知下去了,點擊 notifyEncodeAndRelease() 方法進去看看:
/*DecodeJob*/
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
// (1)通知外面已經完成了
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
// 將資源緩存到磁盤
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
// 完成,釋放各類資源
onEncodeComplete();
}
複製代碼
點擊關注點(1)進去看看:
/*DecodeJob*/
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
複製代碼
這裏的 callback 只有一個實現類就是 EngineJob,因此直接看 EngineJob#onResourceReady():
/*EngineJob*/
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
// 通知結果回調
notifyCallbacksOfResult();
}
複製代碼
繼續跟進:
/*EngineJob*/
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
// 若是取消,則回收和釋放資源
if (isCancelled) {
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
//(1)
engineJobListener.onEngineJobComplete(this, localKey, localResource);
//(2)
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
複製代碼
這裏標記了 2 個關注點,分別以下:
/*Engine*/
@Override
public synchronized void onEngineJobComplete( EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
/*ActiveResources*/
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
複製代碼
這裏主要判斷是否配置了內存緩存,有的話就存到活動緩存中。
/*EngineJob*/
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
synchronized (this) {
copy = cbs.copy();
}
//(2)
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
}
複製代碼
能夠看到,這裏遍歷 copy 後拿到了線程池的執行器(entry.executor),copy 是上面一行 copy = cbs.copy(); 獲得的,咱們倒敘推理一下看這個線程池的執行器是怎麼來的:
// 1. cbs.copy()
/*ResourceCallbacksAndExecutors*/
ResourceCallbacksAndExecutors copy() {
return new ResourceCallbacksAndExecutors(new ArrayList<>(callbacksAndExecutors));
}
// 2. callbacksAndExecutors 集合是哪裏添加元素的
/*ResourceCallbacksAndExecutors*/
void add(ResourceCallback cb, Executor executor) {
callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
}
// 3. 上面的 add() 方法是哪裏調用的
/*EngineJob*/
synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
cbs.add(cb, callbackExecutor);
}
// 4. 上面的 addCallback() 方法是哪裏調用的
/*Engine*/
private <R> LoadStatus waitForExistingOrStartNewJob( ... Executor callbackExecutor, ... ) {
engineJob.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, engineJob);
}
複製代碼
這裏咱們倒敘推理了 4 步,發現是從 waitForExistingOrStartNewJob() 方法那裏傳進來的,繼續日後推的代碼就不列出來了,最後發現是從 into() 方法哪裏傳過來的(能夠本身正序再跟蹤一遍源碼):
/*RequestBuilder*/
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
...
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
複製代碼
也就是這裏的 Executors.mainThreadExecutor(),點進去看看是什麼:
public final class Executors {
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
handler.post(command);
}
};
/** Posts executions to the main thread. */
public static Executor mainThreadExecutor() {
return MAIN_THREAD_EXECUTOR;
}
}
複製代碼
原來這裏是建立了一個 Executor 的實例,而後重寫了 execute() 方法,可是裏面卻用的是主線程中的 Handler 來 post() 一個任務,也就是切換到了主線程去執行了。
再回到關注點(2),發現執行這一句 entry.executor.execute(new CallResourceReady(entry.cb)) 實際執行的是 handler.post(command),因此 CallResourceReady 中的 run() 方法是在主線程中執行的。真是想不到呀,原來子線程切換到主線程在一開始的 into() 方法中就作了鋪墊!
那麼咱們繼續往下看,進入 CallResourceReady#run():
/*EngineJob*/
private class CallResourceReady implements Runnable {
@Override
public void run() {
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
engineResource.acquire();
// 關注點
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
}
複製代碼
繼續跟進:
/*EngineJob*/
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
// 資源準備好了,回調給上一層
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
複製代碼
上面會回調到 SingleRequest#onResourceReady(),進去看看:
/*SingleRequest*/
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
loadStatus = null;
if (resource == null) {
GlideException exception =
new GlideException(...);
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
toRelease = resource;
this.resource = null;
GlideException exception =
new GlideException(...);
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
toRelease = resource;
this.resource = null;
status = Status.COMPLETE;
return;
}
// 關注點
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
複製代碼
能夠看到,前面是一些加載失敗的判斷,看下最後的 onResourceReady():
/*SingleRequest*/
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
...
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
// 回調給 ImageViewTarget
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
// 通知加載成功
notifyLoadSuccess();
}
複製代碼
這裏的 target 爲 ImageViewTarget,進入 ImageViewTarget#onResourceReady() 看看:
/*ImageViewTarget*/
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
/*ImageViewTarget*/
private void setResourceInternal(@Nullable Z resource) {
setResource(resource);
maybeUpdateAnimatable(resource);
}
複製代碼
由於在前面 "2.3.1 GlideContext#buildImageViewTarget()" 中建立的 ImageViewTarget 的實現類是 DrawableImageViewTarget,因此這裏調用的是 DrawableImageViewTarget#setResource(),點進去看看:
/*DrawableImageViewTarget*/
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
複製代碼
到這裏終於將圖片顯示到咱們的 ImageView 中了!into() 方法也結束了。
要我總結的話,只能說 into() 方法實在是太複雜了,with() 與 load() 方法在它面前就是小嘍嘍。 into() 方法主要作了發起網絡請求、緩存數據、解碼並顯示圖片。
大概流程爲: 發起網絡請求前先判斷是否有內存緩存,有則直接從內存緩存那裏獲取數據進行顯示,沒有則判斷是否有磁盤緩存;有磁盤緩存則直接從磁盤緩存那裏獲取數據進行顯示,沒有才發起網絡請求;網絡請求成功後將返回的數據存儲到內存與磁盤緩存(若是有配置),最後將返回的輸入流解碼成 Drawable 顯示在 ImageView 上。
Glide 源碼相對於我以前寫的其餘源碼文章來講確實難啃,可是耐心看完發現收穫也不少。例如利用一個隱藏的 Fragment 來管理 Glide 請求的生命週期,利用四級緩存來提高加載圖片的效率,還有網絡請求成功後是在哪裏切換到主線程的等等。
參考資料: