文章目錄
Glide的用法
Glide.with(this) // 返回RequestManager對象
.load(imageUrl) // 返回Y extends Target<TranscodeType>對象,設置model屬性
.into(imageView); // 最複雜
也可以有其它操作:
// 設置加載尺寸
Glide.with(this).load(imageUrl).override(800, 800).into(imageView);
// 設置加載中以及加載失敗圖片(api裏面對placeholder()、error()函數中有多態實現)
Glide.with(this).load(imageUrl)
.placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).into(imageView);
// 設置加載動畫
Glide.with(this).load(imageUrl).animate(R.anim.item_alpha_in).into(imageView);
// 設置要加載的內容,如圖文混排
Glide.with(this).load(imageUrl).centerCrop().into(new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
imageView.setImageDrawable(resource);
}
});
這裏會根據Glide加載的with、load、into 三個步驟的源碼進行分析瞭解。
一、with
Glide中的with是一組靜態方法,有以下幾個重載,可以傳入Activity、Fragment或者是Context。每個with()方法重載的代碼,都是先調用RequestManagerRetriever的靜態get()方法得到一個RequestManagerRetriever對象(這個靜態get()方法就是一個單例實現)然後再調用RequestManagerRetriever的實例get()方法,去獲取RequestManager對象。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
get的相關代碼就不貼了,關於傳參的部分可以分爲兩類:傳入Application類型的參數 和 傳入非Application類型的參數。
case1(最簡單):如果在Glide.with()方法中傳入的是一個Application對象,或在子線程調用,那麼這裏就會調用getApplicationManager(context)方法來獲取一個RequestManager對象。此時Glide的生命週期和Application對象的生命週期同步。應用程序一旦關閉,Glide的加載也就戛然而止了。
case2:如果傳入的是Activity、FragmentActivity、Fragment等,最終都會向當前的Activity當中添加一個隱藏的Fragment(即fragmentGet、supportFragmentGet方法,分別對應的app包和v4包下的兩種Fragment)。之所以要添加一個隱藏的Fragment,其實和生命週期也有關係。因爲Glide不知道Activity的生命週期,Glide將自己和這個隱藏的Fragment綁在一起,而Fragment的生命週期和Activity是同步的。所以一旦Activity被銷燬了,Glide也就可以及時的停止加載了。
總結來看,就是爲了得到一個RequestManager對象,然後Glide會根據我們傳入with()方法的參數來確定圖片加載的生命週期。
二、load
with方法返回的是一個RequestManager對象,緊接着就調用load方法。可以得知,load方法是屬於RequestManager的。
@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);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
load方法有包含Bitmap 、Drawable、String 、Uri、File等多種類型的重載,最後都是調用 asDrawable() 的load,並將 load()方法中的參數以model傳遞。看一下asDrawable() 方法:
// RequestManager類
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
asDrawable()方法最終會return一個new出來的RequestBuilder對象,換句話說,調用的load方法屬於RequestBuilder類:
// RequestBuilder類
@NonNull
@CheckResult
@SuppressWarnings("unchecked")
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
鬧了半天,load的過程就是爲了設置model得值!
三、into
上一步return了一個new出來的RequestBuilder對象,因此這個into()方法是屬於RequestBuilder對象的:
// RequestBuilder類
@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);
}
上面代碼部分的return值得重點關注
3.1 buildImageViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions
buildImageViewTarget方法位於GlideContext.class:
// GlideContext類
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
#ImageViewTargetFactory.class
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)) {
// load的as傳的是Drawable.class,返回對應的對象
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
load()方法中的 as傳入的參數是 Drawable.class,也就是說,這裏會return一個DrawableImageViewTarget對象。
DrawableImageViewTarget的父類ImageViewTarget#onLoadStarted()會爲view提前設置一些狀態(如placeholder信息等),然後等待Engine後續的加載完成
// ImageViewTarget#onLoadStarted
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
3.2 return的into方法
return的into方法如下:
// RequestBuilder類
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);
// 獲取正在進行的Request請求
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
// 將Request和View進行綁定
target.setRequest(request);
// track請求
requestManager.track(target, request);
return target;
}
這裏要通過Request previous = target.getRequest();獲取當前正在進行的Request請求。然後根據判斷,進行不同的操作。
3.2.1 將Request和View進行綁定,並保存到tag
這裏有一點需要注意,即target指的是ViewTarget(聯繫多層方法調用及泛型即可得知)。下面我們看一下target.setRequest(request);
// ViewTarget#setRequest
// 對Request和View綁定
@Override
public void setRequest(@Nullable Request request) {
setTag(request);
}
// 將這層“綁定關係”保存在tag中
private void setTag(@Nullable Object tag) {
if (tagId == null) {
isTagUsedAtLeastOnce = true;
view.setTag(tag);
} else {
view.setTag(tagId, tag);
}
}
ViewTarget的setRequest方法,其實就是將Request和View進行綁定(相當於卡莉斯塔和牛頭的關係),然後保存在View的tag中。做完這一步,就可以光明正大的執行requestManager.track(target, request);
3.2.2 track當前請求
// RequestManager類
void track(@NonNull Target<?> target, @NonNull Request request) {
// TargetTracker保存了由當前RequestManager處理的所有Target的集合
targetTracker.track(target);
// RequestTracker是針對所有的Request集合
requestTracker.runRequest(request);
}
TargetTracker和RequestTracker分別是對target和request做了一個管理。
重點關注一下runRequest()方法:
// RequestTracker類
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
由isPaused判斷界面的狀態,如果可見,就直接調用request#begin方法執行。否則,就添加到pendingRequests中,來保證Request不會被GC掉(request是WeakHashMap,需強引用防GC)。
防不防GC不重要,重要的是這個request.begin()方法。Request是一個接口,需要看它的實現類SingleRequest(表示正確的請求類,其餘還有縮略圖請求協助類ThumbnailRequestCoordinator、錯誤請求協助類ErrorRequestCoordinator)。
也就是說,request.begin()調用的其實是SingleRequest類中的begin()方法:
// SingleRequest類
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
// 判斷url是否爲null
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
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 we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
// 既然是去加載圖片,就需要獲取加載結果。
// SingleRequest類的接口ResourceCallback,將加載結果告訴Target,Target再去通知View該如何
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
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));
}
}
begin()方法會對url進行檢查判斷,如果爲null就加載失敗。如果size有效,就會觸發真正的請求;否則就設置回調,等待有了size之後再去觸發請求。
順便一提, target.onLoadStarted(getPlaceholderDrawable()),這個onLoadStarted是接口類 Target中的方法,實現類是ViewTarget:
// ViewTarget類
// 調用的父類的onLoadStarted方法
@CallSuper
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
maybeAddAttachStateListener();
}
真正的請求是在onSizeReady()方法中發起的:
// SingleRequest類
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
// 判斷狀態是否是在等待size
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()方法會對狀態進行檢查,判斷是否是在等待size。接着會更新狀態爲Status.RUNNING,然後會調用Engine(包含了所有的參數信息,如緩存,圖片顯示等等),然後去開始真正請求,網絡、內存、磁盤緩存等等。
結合3.1部分,Glide圖片加載的執行流程就結束了。