談到Glide,從英文字面意思有滑行、滑動的意思;而Android從開發的角度我們知道它是一款圖片載入框架,這裡引用官方文件的一句話“Glide是一個快速高效的Android圖片載入庫,注重於平滑的滾動”,從官方文件介紹我們瞭解到用Glide框架來載入圖片是快速並且高效的,接下來就來通過簡單使用Glide和原始碼理解兩個方面看看Glide是否是快速和高效(文中程式碼基於Glide 4.8版本)。
Glide簡單使用
-
1.使用前需要新增依賴
implementation 'com.github.bumptech.glide:glide:4.8.0' //使用Generated API需要引入 annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' 複製程式碼
-
2.簡單載入網路圖片到ImageView,可以看到簡單一句程式碼就能將網路圖片載入到ImageView,也可以使用Generated API方式
//直接使用 Glide.with(Context).load(IMAGE_URL).into(mImageView) //使用Generated API, 作用範圍Application 模組內使用 //建立MyAppGlideModule類加上@GlideModule註解,make project 就能使用 GlideApp @GlideModule public final class MyAppGlideModule extends AppGlideModule {} //Generated API載入圖片 GlideApp.with(Context).load(IMAGE_URL).into(mImageView); 複製程式碼
-
3.當載入網路圖片的時候,網路請求是耗時操作,所以圖片不可能馬上就載入出來,網路請求這段時間ImageView是空白的,所以我們可以使用一個佔位符顯示圖片來優化使用者體驗,佔位符有三種
- 載入佔位符(placeholder)
- 錯誤佔位符(error)
- 後備回撥符(Fallback)
//新增佔點陣圖 RequestOptions requestOptions = new RequestOptions() .placeholder(R.drawable.ic_cloud_download_black_24dp) .error(R.drawable.ic_error_black_24dp) .diskCacheStrategy(DiskCacheStrategy.NONE);//不使用快取 Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView); //Generated API 方式(和Glide3 一樣) GlideApp.with(Context).load(IMAGE_URL) .placeholder(R.drawable.ic_cloud_download_black_24dp) .error(R.drawable.ic_error_black_24dp) .diskCacheStrategy(DiskCacheStrategy.NONE) .into(mImageView); // 後備回撥符(Fallback) Generated API 方式才有,在應用設定使用者頭像場景中,如果使用者不設定,也就是為null的情況,可以使用後備回撥符顯示預設頭像 private static final String NULL_URL=null; GlideApp.with(Context).load(NULL_URL) .fallback(R.drawable.ic_account_circle_black_24dp) .into(mImageView); 複製程式碼
-
4.指定載入圖片的大小(override)
RequestOptions requestOptions = new RequestOptions().override(200,100); Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView); //Generated API 方式 GlideApp.with(Context).load(IMAGE_URL) .override(200,100) .into(mImageView); 複製程式碼
-
5.縮圖 (Thumbnail)
- 這個其實和佔位符(placeholder)有些相似,但是佔位符只能載入本地資源,而縮圖可以載入網路資源,thumbnail方法與我們的主動載入並行執行,如果主動載入已經完成,則縮圖不會顯示
//縮圖Options RequestOptions requestOptions = new RequestOptions() .override(200,100) .diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(Context) .load(IMAGE_URL) .thumbnail( Glide.with(this) .load(IMAGE_URL) .apply(requestOptions)) .into(mImageView); //Generated API 方式 GlideApp.with(Context). load(IMAGE_URL). thumbnail( GlideApp.with(this) .load(IMAGE_URL).override(200,100) .diskCacheStrategy(DiskCacheStrategy.NONE)).into(mImageView); 複製程式碼
-
6.影象變化
-
Glide中內建了三種圖片的變化操作,分別是CenterCrop(圖片原圖的中心區域進行裁剪顯示),FitCenter(圖片原始長寬鋪滿)和CircleCrop(圓形裁剪)
//顯示圓形裁剪到ImageView RequestOptions requestOptions = new RequestOptions() .circleCrop() .diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(Context) .load(IMAGE_URL) .apply(requestOptions) .into(mImageView); //RequestOptions都內建了使用者三種變化的靜態方法 Glide.with(Context) .load(IMAGE_URL) .apply(RequestOptions.circleCropTransform()) .into(mImageView); //Generated API 方式 GlideApp.with(Context).load(IMAGE_URL) .circleCrop() .diskCacheStrategy(DiskCacheStrategy.NONE) .into(mImageView); 複製程式碼
-
如果想要更酷炫的變化,可以使用第三方框架glide-transformations來幫助我們實現,並且變化是可以組合的
//第三方框架glide-transformations引入 implementation 'jp.wasabeef:glide-transformations:4.0.0' //使用glide-transformations框架 變換圖片顏色和加入模糊效果 RequestOptions requestOptions=new RequestOptions() .placeholder(R.drawable.ic_cloud_download_black_24dp) .transforms(new ColorFilterTransformation(Color.argb(80, 255, 0, 0)),new BlurTransformation(30)) .diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(Context).load(IMAGE_URL). apply(requestOptions). into(mImageView); //Generated API 方式 GlideApp.with(Context).load(IMAGE_URL) .transforms(new ColorFilterTransformation(Color.argb(80, 255, 0, 0)),new BlurTransformation(30)) .placeholder(R.drawable.ic_cloud_download_black_24dp) .diskCacheStrategy(DiskCacheStrategy.NONE) .into(mImageView); 複製程式碼
-
更多效果可以檢視官方例子
-
-
7.載入目標(Target)
-
Target是介於請求和請求者之間的中介者的角色,into方法的返回值就是target物件,之前我們一直使用的 into(ImageView) ,它其實是一個輔助方法,它接受一個 ImageView 引數併為其請求的資源型別包裝了一個合適的 ImageViewTarget
//載入 Target<Drawable> target = Glide.with(Context) .load(url) .into(new Target<Drawable>() { ... }); //清除載入 Glide.with(Context).clear(target); 複製程式碼
-
當我們使用Notification顯示應用通知,如果想要自定義通知的介面,我們需要用到RemoteView,如果要給RemoteView設定ImageView,根據提供的setImageViewBitmap方法,如果通知介面需要載入網路圖片,則需要將網路圖片轉換成bitmap,一般我們可以根據獲取圖片連結的流來轉換成bitmap,或者使用本文的主題使用Glide框架,這些都是耗時操作,感覺操作起來很麻煩,而Glide框架很貼心的給我提供了NotificationTarget(繼承SimpleTarget),相對於我們載入目標變成Notification
/** * 新建 NotificationTarget 物件引數說明,與Glide3不同,Glide4的asBitmap()方法必須在load方法前面 * @param context 上下文物件 * @param viewId 需要載入ImageView的view的 id * @param remoteViews RemoteView物件 * @param notification Notification物件 * @param notificationId Notification Id */ String iamgeUrl = "http://p1.music.126.net/fX0HfPMAHJ2L_UeJWsL7ig==/18853325881511874.jpg?param=130y130"; NotificationTarget notificationTarget = new NotificationTarget(mContext,R.id.notification_Image_play,mRemoteViews,mNotification,notifyId); Glide.with(mContext.getApplicationContext()) .asBitmap() .load(iamgeUrl) .into( notificationTarget ); //Generated API 方式 GlideApp.with(mContext.getApplicationContext()) .asBitmap() .load(iamgeUrl) .into( notificationTarget ); 複製程式碼
-
-
8.回撥監聽
- 使用Glide載入圖片,雖然在載入中或者加失敗都有佔位符方法處理,但是我們還是希望可以知道圖片到底是載入成功還是失敗,Glide也給我們提供了監聽方法來知道圖片到底是載入成功還是失敗,結合listener和into方法來使用回撥
Glide.with(this).load(IMAGE_URL). listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { Toast.makeText(getApplicationContext(),"圖片載入失敗",Toast.LENGTH_SHORT).show(); return false; } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { Toast.makeText(getApplicationContext(),"圖片載入成功",Toast.LENGTH_SHORT).show(); return false; } }).into(mImageView);*/ //Generated API 方式 GlideApp.with(this).load(IMAGE_URL) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { Toast.makeText(getApplicationContext(),"圖片載入失敗",Toast.LENGTH_SHORT).show(); return false; } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { Toast.makeText(getApplicationContext(),"圖片載入成功",Toast.LENGTH_SHORT).show(); return false; } }).into(mImageView); 複製程式碼
- 可以看到監聽實現的方法都有布林型別的返回值,返回true,則代表處理了該回撥事件,false則不進行處理,如果onResourceReady方法返回true,則into方法就不會執行,也就是圖片不會載入到ImageView,同理onLoadFailed方法返回true,則error方法不會執行。
Glide還有其他的一些使用方法,這裡就不繼續展開了,有興趣的可以自行繼續研究。
Glide原始碼解析
Glide載入圖片到ImageView基本流程圖
Glide載入圖片到ImageView原始碼分析
- 在上一節簡單的列出了一些Glide的使用方法,能用不代表你已經懂了,接下來就通過理解原始碼的方式來對Glide是如何工作的做深一層次理解,首先從最簡單使用開始
Glide.with(Context).load(IMAGE_URL).into(mImageView);
複製程式碼
with方法
- 來吧,開始是Glide的with()方法,直接上原始碼
/** Glide類的with()方法*/
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
複製程式碼
- 通過原始碼,可以看到with有不同引數型別的過載方法,每個方法首先都是呼叫 getRetriever()方法
/** Glide類的getRetriever()方法*/
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
複製程式碼
- Glide的get方法中通過new GlideBuilder()獲取了Glide物件,並通過Glide的getRequestManagerRetriever()的方法最終得到RequestManagerRetriever物件,接下來我們看看RequestManagerRetriever物件的get方法
/** RequestManagerRetriever類的get()方法*/
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
// The view might be somewhere else, like a service.
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
複製程式碼
- 同樣,RequestManagerRetriever物件的get方法也有不同型別引數的過載,分別針對Application、Activity、Fragmenet、view做了不同的處理,先看Context引數的get方法,在該方法中它把Context的引數分成了兩個型別,一個Application型別的Context,另一個是非Application型別的Context。如果是Application型別的Context,則建立的Glide的生命週期則跟隨ApplicationContext的生命週期,也就是下面的getApplicationManager所做的事情。
/** RequestManagerRetriever類的getApplicationManager()方法*/
@NonNull
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) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
複製程式碼
- 接著,如果是非Application型別的,Activity、Fragmenet屬於非Application;如果是Activity型別的Context,當前不再主執行緒,則繼續跟隨Application生命週期,否則給當前Activity新增一個隱藏的Fragment,然後Glide生命週期跟隨這個隱藏的Fragment,分析到這裡,我們再看Fragmenet型別的Context,或者是View型別,也是新增了一個隱藏的Fragment。這是為什麼呢?首先Fragment的生命週期是和Activity同步的,Activity銷燬Fragment也會銷燬,其次,這也方便Glide知道自己什麼時候需要停止載入,如果我們開啟一個Activity並關閉它,如果Glide生命週期跟隨Application,則Activity雖然已經銷燬,但是應用還沒退出,則Glide還在繼續載入圖片,這顯然是不合理的,而Glide很巧妙的用一個隱藏Fragment來解決生命週期的監聽。
/** RequestManagerRetriever類的fragmentGet()方法*/
@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@Deprecated
@NonNull
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
/** RequestManagerRetriever類的getRequestManagerFragment()方法*/
@SuppressWarnings("deprecation")
@NonNull
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
複製程式碼
- 經過對into方法的分析,最終獲取的是跟隨對應Context物件生命週期的RequestManager物件。
load方法
- 經過上一小節的分析,Glide.with方法最終獲取的是RequestManager物件,所以繼續看RequestManager物件裡面load方法,
/** RequestManager 類的as()方法*/
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
/** RequestManager 類的as()方法*/
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
/** RequestManager 類的部分load()方法*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
//省略其他引數型別 load() 方法
.......
複製程式碼
- 通過以上load方法,可以發現雖然RequestManager物件的load方法有多個型別引數的過載,但是不管load方法傳遞什麼型別引數,該方法都是呼叫RequestBuilder物件的load方法
/** RequestBuilder 類的load()方法*/
@NonNull
@CheckResult
@SuppressWarnings("unchecked")
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
/** RequestBuilder物件 類的loadGeneric()方法*/
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
複製程式碼
- 通過以上RequestBuilder物件的load()方法,我們可以明白不管RequestManager物件的load方法方法傳遞什麼型別的載入資源引數,RequestBuilder物件都把它看成時Object物件,並在loadGeneric方法中賦值給RequestBuilder物件的model物件。
- 通過檢視RequestBuilder物件,我們還注意到apply(RequestOptions)這個方法,前面我們的例子中使用快取,載入影象大小,設定載入佔位符和錯誤佔位符都需要新建RequestOptions物件,並設定我們的配置,現在我們分析的載入並沒有apply一個RequestOptions物件,則Glide會使用requestOptions.clone()去載入預設配置,這裡就先不進行展開了,先繼續關注接下來的into方法。
/** RequestBuilder 類的apply方法*/
@NonNull
@CheckResult
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
@SuppressWarnings("ReferenceEquality")
@NonNull
protected RequestOptions getMutableOptions() {
return defaultRequestOptions == this.requestOptions
? this.requestOptions.clone() : this.requestOptions;
}
複製程式碼
- 經過以上對with()方法和load方法的分析,經過這兩步之後得到了RequestBuilder物件,也就說明真正的圖片載入操作是在into方法來完成,也就是RequestBuilder物件的into方法。
into方法
- 通過上一小節的分析,經過load方法之後獲取的物件是RequestBuilder,並且我們將load方法的引數賦值給了RequestBuilder物件的model引數,接下來就到了Glide最核心的方法,也就是RequestBuilder物件的into方法
獲取DrawableImageViewTarget
/** RequestBuilder 類的into方法*/
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
複製程式碼
- RequestBuilder 物件的into方法中首先獲取傳遞進來的ImageView的ScaleType,讓Glide載入出來的ImageView保持一樣的ScaleType變化,然後我們看到最後一句話,該方法返回了RequestBuilder 物件的另一個into方法,先看glideContext.buildImageViewTarget()做了什麼操作
/** GlideContext 類的 buildImageViewTarget方法*/
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
//省略程式碼
.....
}
}
}
複製程式碼
- 通過以上原始碼,之前我們看RequestBuilder原始碼中as方法傳入的是Drawable.class,所以以上的buildImageViewTarget方法最終返回的是DrawableImageViewTarget物件,接著我們繼續看第一步into方法返回into方法中做了什麼操作
構建Request
/** RequestBuilder 類的into方法返回的into方法*/
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
//省略部分程式碼
......
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
複製程式碼
- 通過以上原始碼,我們應該先明白Request類是一個介面,他抽象了Glide載入圖片請求(Request類原始碼這裡就不貼了),它是一個非常重要的類,這裡我們先看看buildRequest(target, targetListener, options)方法是如何建立Request物件的
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions);
}
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
//省略部分程式碼 error Request build
.....
Request mainRequest =
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions);
if (errorRequestCoordinator == null) {
return mainRequest;
}
//省略部分程式碼 error Request build
.....
}
private Request buildThumbnailRequestRecursive(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
if (thumbnailBuilder != null) {
//省略部分程式碼 縮圖操作
.....
} else {
// Base case: no thumbnail.
return obtainRequest(
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
}
}
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
複製程式碼
- 通過上面的原始碼,我們可以看到buildRequest方法呼叫了buildRequestRecursive方法,在buildRequestRecursive方法中大部分程式碼都在處理縮圖(thumbnail),我們主流程中沒有設定縮圖,這裡就不進行展開分析,接著buildRequestRecursive方法又呼叫了obtainRequest方法,obtainRequest方法傳遞了非常多引數,比如有我們熟悉的RequestOptions,設定圖片尺寸的 overrideWidth, overrideHeight,還有第一步into方法中的target物件,也就是DrawableImageViewTarget物件,model也就是我們load傳入的圖片地址,也就說明不管load方法還是apply方法傳入的引數最終都給到了這裡傳入SingleRequest.obtain方法,我們繼續看看SingleRequest類
public final class SingleRequest<R> implements Request,
SizeReadyCallback,
ResourceCallback,
FactoryPools.Poolable {
//省略部分程式碼
......
/**SingleRequest類的 obtain方法*/
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
RequestOptions requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory) {
@SuppressWarnings("unchecked") SingleRequest<R> request =
(SingleRequest<R>) POOL.acquire();
if (request == null) {
request = new SingleRequest<>();
}
request.init(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
engine,
animationFactory);
return request;
}
//省略部分程式碼
......
}
複製程式碼
- 通過SingleRequest物件的obtain方法,我們可以看到request = new SingleRequest<>();也就是最終我們構建的Request是SingleRequest物件,並在init方法中將上一步obtainRequest方法傳遞進來的各種引數進行賦值。
Request執行
- 構建完成Request物件,接下來繼續看剛剛的into方法下面的操作
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target
複製程式碼
- 首先RequestManager物件清除target,此時不懂你是否還記得RequestManager,該物件是第一步with方法之後得到的,接著是將我們上一步得到的SingleRequest物件設定給target,接著又執行了RequestManager.track方法,繼續跟進該方法看看
/** RequestManager 類的track方法*/
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
/** RequestTracker 類的runRequest方法*/
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);
}
}
複製程式碼
- 通過上面的原始碼,RequestManager物件的track方法中執行了RequestTracker 類的runRequest方法,該方法中簡單判斷當前Glide是否在暫停狀態,不是暫停狀態則執行Request的begin方法,否則將這個Request加入到請求佇列ListpendingRequests中.
後備回撥符、載入佔位符和錯誤佔位符載入
- 接下來我們看看Request的begin方法到底幹了啥,要找到begin方法實現,根據前面分析,我們則應該去看SingleRequest物件的begin方法
/** SingleRequest 類的begin方法*/
@Override
public void begin() {
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;
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));
}
}
private void onLoadFailed(GlideException e, int maxLogLevel) {
//省略部分程式碼
.......
if (!anyListenerHandledUpdatingTarget) {
setErrorPlaceholder();
}
//省略部分程式碼
.......
}
複製程式碼
- 通過以上begin方法原始碼,如果model為空,也就是我們load傳入的圖片地址為空,則會呼叫onLoadFailed方法,而onLoadFailed方法又呼叫了setErrorPlaceholder方法,接著看看該方法中做了什麼操作
/** SingleRequest 類的setErrorPlaceholder方法*/
private void setErrorPlaceholder() {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = null;
if (model == null) {
error = getFallbackDrawable();
}
// Either the model isn't null, or there was no fallback drawable set.
if (error == null) {
error = getErrorDrawable();
}
// The model isn't null, no fallback drawable was set or no error drawable was set.
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(error);
}
private Drawable getErrorDrawable() {
if (errorDrawable == null) {
errorDrawable = requestOptions.getErrorPlaceholder();
if (errorDrawable == null && requestOptions.getErrorId() > 0) {
errorDrawable = loadDrawable(requestOptions.getErrorId());
}
}
return errorDrawable;
}
複製程式碼
- 通過以上原始碼,如果我們傳入圖片地址為空,則首先檢視是否有後備回撥符設定,然後是錯誤佔位符,最後是載入佔位符,最終呼叫target.onLoadFailed方法,也就是ImageViewTarget的onLoadFailed方法
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {
//省略部分程式碼
.......
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
//省略部分程式碼
.......
}
複製程式碼
- 通過以上原始碼,我想你應該已經明白了後備回撥符、錯誤佔位符載入,這裡還有一個疑問,載入佔位符呢?我們回到之前SingleRequest物件的begin方法,相信你會馬上看到在載入狀態為RUNNING的時候呼叫了target.onLoadStarted,也實現了載入中的佔位符,到這裡我們已經分析完了後備回撥符、載入佔位符和錯誤佔位符載入底層實現邏輯。
載入圖片網路請求
- 前面分析完各種佔位符實現,我們再次回到SingleRequest物件的begin方法,我們可以注意到onSizeReady()和target.getSize()這兩句就是載入圖片的入口,如果我們在使用glide的時候設定圖片載入的大小尺寸,則會呼叫target.getSize()
/** ViewTarget 類的etSize方法*/
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
//省略部分程式碼
......
}
複製程式碼
- 通過以上原始碼,target.getSize()會根據ImageView的寬高來得出圖片的載入寬高,最終target.getSize()還是會呼叫onSizeReady()方法,所以我們就直接來看看SingleRequest物件onSizeReady()方法中做了什麼操作。
/** SingleRequest 類的onSizeReady方法*/
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
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);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
複製程式碼
- 在onSizeReady方法中,主要呼叫了engine.load()方法並返回載入狀態,engine.load方法繼續接收我們之前傳入的各種引數,其中也有我們model物件,也就是之前load方法傳入的圖片地址。首先我們需要了解engine是什麼,顧名思義,engine的英文意思是發動機,而在Glide框架中他就是負責啟動圖片載入的發動機,主要負責啟動載入,我們在前面with方法獲取glide物件中得到了engine物件(這裡就不貼原始碼了),我們接著看engine.load()方法進行什麼操作
/** Engine 類的load方法*/
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) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
/** DecodeJob 類的繼承關係*/
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable
複製程式碼
- 通過以上原始碼,Engine物件的load方前面一段程式碼都是在處理快取問題,這裡先不進行展開,繼續走我們載入圖片的主線,往下看我們看到構建了一個EngineJob物件,還構建了一個DecodeJob物件,構建DecodeJob物件又繼續接收我們之前傳入的各種引數,由DecodeJob物件的繼承關係我們可以知道它是Runnable物件,接著我們看到engineJob的start()方法,它直接傳入了DecodeJob物件
/** EngineJob 類的start方法*/
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
/** GlideExecutor 類的newSourceExecutor方法*/
public static GlideExecutor newSourceExecutor(
int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
return new GlideExecutor(
new ThreadPoolExecutor(
threadCount /* corePoolSize */,
threadCount /* maximumPoolSize */,
0 /* keepAliveTime */,
TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(),
new DefaultThreadFactory(name, uncaughtThrowableStrategy, false)));
}
複製程式碼
- 通過以上原始碼,EngineJob物件的start方法首先還是判斷快取,最終獲取的GlideExecutor就是一個執行緒池執行器(Executor),GlideExecutor中有各種方法獲得快取執行緒池,還有資源執行緒池(SourceExecutor),以上原始碼貼出資源執行緒池。實際上EngineJob物件的start方法就是用來線上程池中啟動DecodeJob這個Runnable物件,也就是說EngineJob的主要作用是開啟執行緒來載入圖片,接著我們來看看DecodeJob物件的run方法。
/** DecodeJob 類的run方法*/
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
//省略部分程式碼
.......
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
/** DecodeJob 類的runWrapped方法*/
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
/** DecodeJob 類的getNextStage方法*/
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 類的getNextGenerator方法*/
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);
}
/** DecodeJob 類的runGenerators方法*/
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
//省略部分程式碼
.......
}
複製程式碼
- 上面我們再次貼出了一堆程式碼,我們來好好梳理一下邏輯,DecodeJob物件的run方法中邏輯很簡單,就是呼叫了自身的runWrapped方法,runWrapped方法中首先判斷Stage列舉,前面在建立DecodeJob物件時候設定初始狀態為Stage.INITIALIZE,然後接著呼叫getNextStage方法,這裡我們還是繼續跳過快取,所以getNextStage方法最終返回的是Stage.SOURCE狀態,接著在getNextGenerator()方法中我們獲取就是SourceGenerator物件,也就是run方法中的第一句話DataFetcher<?> localFetcher = currentFetcher中localFetcher就是我們剛剛獲得的SourceGenerator物件,接著繼續執行runGenerators()方法,在該方法的while迴圈判斷條件執行了currentGenerator.startNext()方法,也就是SourceGenerator物件的startNext()方法
/** SourceGenerator 類的startNext()方法*/
@Override
public boolean startNext() {
//省略部分程式碼,跳過快取部分判斷
........
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
/** DecodeHelper 類的getLoadData() 方法*/
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;
}
/** HttpGlideUrlLoader 類的buildLoadData 方法*/
@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=helper.getLoadData().get(loadDataListIndex++)這一句程式碼,helper就是DecodeHelper物件,在我們前面建立DecodeJob物件的時候已經把它建立,之前我們在load步驟中傳入的model是圖片url地址,所以經過DecodeHelper 類的getLoadData() 方法(更細的程式碼這裡就不進行展開了),最終獲取的ModelLoader<Object, ?> modelLoader物件則為HttpGlideUrlLoader物件,也就是laodData物件,所以modelLoader.buildLoadData建立則在HttpGlideUrlLoader物件的buildLoadData中實現,上方貼出的該方法原始碼中把我們model賦值給GlideUrl物件,也就是將其作為URL地址來進行處理,則經過modelLoader.buildLoadData獲取的loadData.fetcher則對應HttpUrlFetcher物件,所以loadData.fetcher.loadData呼叫的就是HttpUrlFetcher物件loadData方法,
/**HttpUrlFetcher類的loadData方法 **/
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
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));
}
}
}
/**HttpUrlFetcher類的loadDataWithRedirects方法 **/
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
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)) {
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物件的loadData方法首先呼叫自身loadDataWithRedirects方法,接著我們看到該方法原始碼,這裡使用了HttpURLConnection來執行了網路請求,看到這裡內心還是有點開心的,前面看了這麼多原始碼,終於看到Glide的網路請求了,開心之後還沒完呢,還得接著往下看,執行完網路請求成功,loadDataWithRedirects方法中網路請求成功呼叫getStreamForSuccessfulRequest返回了一個InputStream流(記住這個InputStream,很關鍵),然後執行了一個callback回撥,而這個回撥物件就是我們之前在SourceGenerator物件中呼叫loadData方法傳入SourceGenerator物件本身,所以callback.onDataReady()呼叫的就是SourceGenerator物件的onDataReady方法
/**SourceGenerator類的onDataReady方法 **/
@Override
public void onDataReady(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);
}
}
複製程式碼
- 通過以上原始碼,不走快取的情況下則呼叫cb.onDataFetcherReady,這個cb也就是前面我們new SourceGenerator物件傳入的 DecodeJob物件,也就是呼叫DecodeJob物件onDataFetcherReady方法
/**DecodeJob類的onDataFetcherReady方法 **/
@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 {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
複製程式碼
- 通過以上原始碼,onDataFetcherReady方法中將之前網路請求得到的流賦值給當前的DecodeJob物件的currentData,其他資料都賦值給對應欄位,最終呼叫的是decodeFromRetrievedData方法
載入圖片(解碼,轉碼)
/**DecodeJob類的decodeFromRetrievedData方法 **/
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
/**DecodeJob類的decodeFromData方法 **/
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類的decodeFromFetcher方法 **/
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
複製程式碼
- 通過以上原始碼,decodeFromRetrievedData方法呼叫了decodeFromFetcher方法,在該方法中首先通過decodeHelper.getLoadPath獲取LoadPath物件,LoadPath物件其實是根據我們傳入的處理資料來返回特定的資料解碼轉碼處理器,我們跟進decodeHelper.getLoadPath看看
/** DecodeHelper類的getLoadPath方法*/
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
/** Registry類的getLoadPath方法*/
@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
//省略部分程式碼
.......
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
/** Registry類的getDecodePaths方法*/
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
//省略部分程式碼,去除干擾
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
decoders, transcoder, throwableListPool);
decodePaths.add(path);
}
return decodePaths;
}
/** Registry類的getDecoders方法*/
public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(@NonNull Class<T> dataClass,
@NonNull Class<R> resourceClass) {
List<ResourceDecoder<T, R>> result = new ArrayList<>();
for (String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry<?, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)) {
result.add((ResourceDecoder<T, R>) entry.decoder);
}
}
}
// TODO: cache result list.
return result;
}
複製程式碼
- 通過以上原始碼,我們接著前面跟進DecodeHelper.getLoadPath方法,它呼叫了Registry物件的getLoadPath方法,Registry物件的getLoadPath方法又呼叫了自身的getDecodePaths方法,現在我前面提到過得我們網路請求獲取的是InputStream流,所以上面原始碼getDecodePaths方法中Data泛型就是InputStream,在根據getDecoders方法遍歷得到解碼器ResourceDecoder能處理InputStream流的有StreamBitmapDecoder和StreamGifDecoder,StreamGifDecoder處理的是Gif,我們這裡處理圖片就之能是StreamBitmapDecoder,它將InputStream流解碼成bitmap,然後能將bitmap轉換成Drawable的轉碼器ResourceTranscoder物件則是BitmapDrawableTranscoder,最後getDecodePaths將我們剛剛分析得到的解碼器和轉碼器傳遞給了新建的DecodePath物件,DecodePath物件就是用來幫助我們進行解碼和轉碼的。
- 接著我們繼續上一步的decodeFromFetcher方法,該方法返回的runLoadPath最終呼叫了上面獲得的DecodePath物件的decode方法
/**DecodePath類的decode方法**/
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
/**DecodePath類的decodeResource方法**/
@NonNull
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類的decodeResourceWithList方法**/
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
//省略部分程式碼
........
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);
}
//省略部分程式碼
........
return result;
}
複製程式碼
- 通過以上原始碼,DecodePath物件的decode方法呼叫了decodeResource方法,decodeResource又呼叫了decodeResourceWithList方法,經過前面分析,decodeResourceWithList方法中獲得的decoder就是前面提到的解碼器StreamBitmapDecoder物件,所以我們接著看StreamBitmapDecoder的decode方法
/**StreamBitmapDecoder類的decode方法**/
@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;
}
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
複製程式碼
- 通過以上原始碼,StreamBitmapDecoder的decode方法中只是對InputStream進行包裝(裝飾模式),可以讓Glide進行更多操作,最終呼叫了downsampler.decode,這個downsampler物件則是Downsampler物件(英文註釋:Downsamples, decodes, and rotates images according to their exif orientation.),英文註釋大致意思是對影象exif格式進行取樣、解碼和旋轉。而我們這裡呼叫了它的decode方法,也就是對我們前面包裝的流進行解碼
/**Downsampler類的decode方法**/
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
//省略部分程式碼
try {
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
/**Downsampler類的decodeFromWrappedStreams方法**/
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
//省略部分程式碼
.........
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
//省略部分程式碼
.........
Bitmap rotated = null;
if (downsampled != null) {
//縮放效正處理
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
/**Downsampler類的decodeStream方法**/
private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options,
DecodeCallbacks callbacks, BitmapPool bitmapPool) throws IOException {
if (options.inJustDecodeBounds) {
is.mark(MARK_POSITION);
} else {
callbacks.onObtainBounds();
}
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;
String outMimeType = options.outMimeType;
final Bitmap result;
TransformationUtils.getBitmapDrawableLock().lock();
try {
result = BitmapFactory.decodeStream(is, null, options);
} catch (IllegalArgumentException e) {
//省略部分程式碼
.........
return result;
}
複製程式碼
- 通過以上原始碼,Downsampler物件的decode方法首先呼叫了decodeFromWrappedStreams方法,在decodeFromWrappedStreams方法中又呼叫了decodeStream方法,在該方法中呼叫用了BitmapFactory.decodeStream,到這裡我們終於看到了Glide將InputStream流解析成了bitmap,而最終Downsampler物件的decode方法返回的Resource物件就是BitmapResource物件
- 經過前面的分析,Glide已經將InputStream解碼完成,這時我們還得再次回到DecodePath物件的decode方法,解碼完成還需轉碼,這裡再次貼一下ecodePath物件的decode方法,前面已經分析了轉碼器為BitmapDrawableTranscoder物件,所以我們繼續看BitmapDrawableTranscoder物件transcode做了什麼操作
/**DecodePath類的decode方法**/
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
/**BitmapDrawableTranscoder類的transcode方法**/
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
/**LazyBitmapDrawableResource類的obtain方法**/
@Nullable
public static Resource<BitmapDrawable> obtain(
@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
複製程式碼
- 通過以上原始碼,BitmapDrawableTranscoder物件transcode方法最終返回了LazyBitmapDrawableResource物件,也就是將我們解碼拿到的BitmapResource物件轉換成了LazyBitmapDrawableResource物件
到此,Glide整個圖片解碼轉碼已近完成,接著我們再回到DecodeJob物件的decodeFromRetrievedData方法
圖片顯示
/**DecodeJob類的decodeFromRetrievedData方法**/
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
/**DecodeJob類的notifyEncodeAndRelease方法**/
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
//省略部分程式碼
.....
Resource<R> result = resource;
//省略部分程式碼
.....
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
//省略部分程式碼
.....
}
/**DecodeJob類的notifyComplete方法**/
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
複製程式碼
- 通過以上原始碼,DecodeJob物件的decodeFromRetrievedData方法呼叫notifyEncodeAndRelease方法,將我們上一步獲取的LazyBitmapDrawableResource傳入notifyComplete方法中,在notifyComplete呼叫了callback.onResourceReady,而這個callback物件就是EngineJob物件(它實現了DecodeJob.Callback介面),也許到這裡你已經忘了EngineJob物件是什麼,前面我們開啟執行緒執行載入的start方法就在EngineJob物件中,所以我們去看看EngineJob物件的onResourceReady方法
private static final Handler MAIN_THREAD_HANDLER =
new Handler(Looper.getMainLooper(), new MainThreadCallback());
/**EngineJob類的onResourceReady方法**/
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
/**EngineJob類的handleMessage方法**/
@Override
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
case MSG_EXCEPTION:
job.handleExceptionOnMainThread();
break;
case MSG_CANCELLED:
job.handleCancelledOnMainThread();
break;
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
/**EngineJob類的handleResultOnMainThread方法**/
@Synthetic
void handleResultOnMainThread() {
//省略部分程式碼
......
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
//省略部分程式碼
......
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
//省略部分程式碼
......
}
/**EngineJob類的addCallback方法**/
private final List<ResourceCallback> cbs = new ArrayList<>(2);
void addCallback(ResourceCallback cb) {
Util.assertMainThread();
stateVerifier.throwIfRecycled();
if (hasResource) {
cb.onResourceReady(engineResource, dataSource);
} else if (hasLoadFailed) {
cb.onLoadFailed(exception);
} else {
cbs.add(cb);
}
}
複製程式碼
- 通過以上原始碼,前面我們在Engine類的Load方法中已經將SingleRequest這個物件通過EngineJob物件的addCallback方法加入到了cbs這個List當中,EngineJob物件的onResourceReady方法中將我們載入好的圖片物件通過Hanlder將資料又傳遞到了主執行緒(主執行緒更新UI),也就是handleResultOnMainThread方法中根據我們剛剛的分析通過cb.onResourceReady將資料回撥通知,cb物件就是SingleRequest物件,我們接著看SingleRequest物件的onResourceReady回撥方法
/**SingleRequest類的onResourceReady回撥方法**/
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
//省略部分程式碼
.......
Object received = resource.get();
//省略部分程式碼
.......
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
/**SingleRequest類的onResourceReady方法**/
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
//省略部分程式碼
.......
status = Status.COMPLETE;
this.resource = resource;
//省略部分程式碼
.......
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
}
//省略部分程式碼
.......
}
複製程式碼
通過以上原始碼,這時候我們已經可以看到長征勝利的曙光了,SingleRequest物件的onResourceReady回撥方法中呼叫了resource.get(),而這個resource就是前面我們經過解碼、轉碼獲取的LazyBitmapDrawableResource物件,然後又呼叫了SingleRequest物件的onResourceReady私有方法,在該方法中又呼叫了target.onResourceReady方法,在我們最開始進入into方法的時候我們已經分析過建立的target物件就是DrawableImageViewTarget物件,它繼承了抽象類ImageViewTarget,所以我們看看抽象類ImageViewTarget的onResourceReady方法
/** LazyBitmapDrawableResource類的get()方法**/
@NonNull
@Override
public BitmapDrawable get() {
return new BitmapDrawable(resources, bitmapResource.get());
}
/** ImageViewTarget類的onResourceReady方法**/
@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類的setResourceInternal方法**/
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected abstract void setResource(@Nullable Z resource);
複製程式碼
- 通過以上原始碼,LazyBitmapDrawableResource物件的get()方法獲取了BitmapDrawable(實際就是Drawable物件),ImageViewTarget物件的onResourceReady方法通過前面分析被呼叫,然後該方法再呼叫了ImageViewTarget物件setResourceInternal方法,setResourceInternal方法最終setResource方法,setResource在ImageViewTarget物件是抽象方法,它在DrawableImageViewTarget物件中實現,最後我們看看DrawableImageViewTarget物件setResource方法
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
複製程式碼
到此,我們的萬里長征終於結束了,Glide的簡單載入圖片流程已經分析完了。
最後說點
- 最後我還是想要把那句簡單的程式碼給貼出來
Glide.with(Context).load(IMAGE_URL).into(mImageView);
複製程式碼
-
就是這樣一句簡單的程式碼,它背後所走的邏輯卻讓人頭皮發麻,此時我只想說一句話“read the fuck source code”。前面我們只是分析了Glide簡單的載入圖片流程,它的快取使用,回撥等功能原理還沒分析到,這隻能等到下篇文章了。文章中如果有錯誤,請大家給我提出來,大家一起學習進步,如果覺得我的文章給予你幫助,也請給我一個喜歡和關注,同時也歡迎訪問我的個人部落格。
-
對本文感興趣的朋友請繼續閱讀從原始碼角度深入理解Glide(下)
-
參考連結