在大多數(shù)的時候使用Gilde只用一行代碼:
Glide.with(this).load(url).into(imageView);
對于源碼學(xué)習(xí)拼缝,一開始喜歡逐行逐句的去看喇闸,后來發(fā)現(xiàn)自己非常容易陷入其中,其實(shí)只需要我們從常用的功能點(diǎn)入手去分析,明白主題邏輯就夠了
本文是基于4.0.0學(xué)習(xí)分析的
第一個方法with()
這是Gilde類提供的一組靜態(tài)方法杈帐,有多個重載的方法
public static RequestManager with(Context context) {
return getRetriever(context).get(context);
}
/**
* Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle
* and that uses the given {@link Activity}'s default options.
*
* @param activity The activity to use.
* @return A RequestManager for the given activity that can be used to start a load.
*/
public static RequestManager with(Activity activity) {
return getRetriever(activity).get(activity);
}
/**
* Begin a load with Glide that will tied to the give
* {@link android.support.v4.app.FragmentActivity}'s lifecycle and that uses the given
* {@link android.support.v4.app.FragmentActivity}'s default options.
*
* @param activity The activity to use.
* @return A RequestManager for the given FragmentActivity that can be used to start a load.
*/
public static RequestManager with(FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
/**
* Begin a load with Glide that will be tied to the given {@link android.app.Fragment}'s lifecycle
* and that uses the given {@link android.app.Fragment}'s default options.
*
* @param fragment The fragment to use.
* @return A RequestManager for the given Fragment that can be used to start a load.
*/
public static RequestManager with(android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
/**
* Begin a load with Glide that will be tied to the given
* {@link android.support.v4.app.Fragment}'s lifecycle and that uses the given
* {@link android.support.v4.app.Fragment}'s default options.
*
* @param fragment The fragment to use.
* @return A RequestManager for the given Fragment that can be used to start a load.
*/
public static RequestManager with(Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
可以看到重載的種類非常多,參數(shù)可以傳入Context专钉,Activity挑童,fragment,fragmentActivity等跃须,方法也是很簡單:getRetriever()獲取了RequestManagerRetriever對象站叼,再通過get()方法獲取了RequestManager ()對象,
下面分析一下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();
}
首先檢查了context的傳入是否為空菇民,若為空則拋出異常尽楔,接下來獲取Glide實(shí)例并去獲取RequestManagerRetriever實(shí)例
接下來再來分析一下RequestManagerRetriever的get()方法干了啥
public RequestManager get(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);
}
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, null /*parentHint*/);
}
}
public RequestManager get(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);
}
}
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, null /*parentHint*/);
}
}
public RequestManager get(View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
可以看到這也是一組重載方法,看上去有好多個參數(shù)第练,實(shí)際上只要分清楚是Application和非Application就行阔馋,
我們先看Application的情況,如果在Glide.with()方法中傳入的是一個Application對象娇掏,那么這里就會調(diào)用帶有Context參數(shù)的get()方法重載呕寝,然后getApplicationManager()方法來獲取一個RequestManager對象。其實(shí)這是最簡單的一種情況婴梧,因?yàn)锳pplication對象的生命周期即應(yīng)用程序的生命周期下梢,因此Glide并不需要做什么特殊的處理,它自動就是和應(yīng)用程序的生命周期是同步的塞蹭,如果應(yīng)用程序關(guān)閉的話孽江,Glide的加載也會同時終止。
接下來我們看傳入非Application參數(shù)的情況番电。不管你在Glide.with()方法中傳入的是Activity竟坛、FragmentActivity永丝、v4包下的Fragment、還是app包下的Fragment垦巴,最終的流程都是一樣的明也,那就是會向當(dāng)前的Activity當(dāng)中添加一個隱藏的沒有視圖的Fragment,通過與fragment的生命周期進(jìn)行關(guān)聯(lián)進(jìn)而實(shí)現(xiàn)組件的生命周期的實(shí)現(xiàn)崭歧,
另外如果我們是在非主線程當(dāng)中使用的Glide隅很,那么不管你是傳入的Activity還是Fragment,都會被強(qiáng)制當(dāng)成Application來處理
總結(jié):
Glide:初始化各個組件的使用入口
RequestManagerRetriever:
是用來創(chuàng)建并從activity和fragment中檢索已存在的RequestManager率碾;
RequestManagerFragment:
沒有視圖的fragment叔营,簡單的來講,就是在每一個Activity或者Fragment上又添加了一個Fragment所宰,該Fragment沒有View绒尊,僅僅用來存儲RequestManager并管理Glide請求
Request Manager:
通過 ActivityFragmentLifecycle 的 addListener 方法注冊 LifecycleListener。當(dāng) RequestManagerFragment 生命周期方法執(zhí)行的時候仔粥,觸發(fā) ActivityFragmentLifecycle 的相應(yīng)方法婴谱,然后會遍歷所有注冊的 LifecycleListener 并執(zhí)行相應(yīng)生命周期方法。這樣就完成了生命周期和Glide的request請求的完整關(guān)聯(lián)躯泰。
總體來說谭羔,第一個with()方法的源碼還是比較好理解的。其實(shí)就是為了得到一個RequestManager對象而已麦向,然后Glide會根據(jù)我們傳入with()方法的參數(shù)來確定圖片加載的生命周期
第二個方法load()
由于with方法返回的是一個RequestManager的對象瘟裸,接下來的這個方法肯定是在RequestManager類中,只有一個方法诵竭,與3.7.0不同沒有那么多的重載方法:
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class).transition(new DrawableTransitionOptions());
}
創(chuàng)建一個RequestBuilder话告,并添加一個過場動畫
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
這一步?jīng)]有什么內(nèi)容,都是在給requestBuilder參數(shù)賦值卵慰,并返回一個對象為下一步做準(zhǔn)備
第三個方法into()
with返回的是RequestManger的對象
load返回的是RequestBuilder的對象
into返回的是Target的對象
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions.optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter();
break;
case FIT_XY:
requestOptions.optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(context.buildImageViewTarget(view, transcodeClass));
首先Util.assertMainThread();判斷是否是在主線程沙郭,如果不是拋出異常,
然后Preconditions.checkNotNull(view);判斷view是否為空呵燕,若為空則拋出異常棠绘,通過判斷view的scaleType設(shè)置圖片的加載形式
public <X> Target<X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
這里其實(shí)又是調(diào)用了ImageViewTargetFactory的buildTarget()方法,我們繼續(xù)跟進(jìn)去再扭,代碼如下所示:
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
可以看到氧苍,在buildTarget()方法中會根據(jù)傳入的class參數(shù)來構(gòu)建不同的Target對象,這個class參數(shù)其實(shí)基本上只有兩種情況泛范,如果你在使用Glide加載圖片的時候調(diào)用了asBitmap()方法让虐,那么這里就會構(gòu)建出BitmapImageViewTarget對象,否則的話構(gòu)建的都是GlideDrawableImageViewTarget對象罢荡,我們使用的事asDrawable
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request previous = target.getRequest();
if (previous != null) {
requestManager.clear(target);
}
requestOptions.lock();
Request request = buildRequest(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
再次檢查是否是在主線程赡突,以及view是否為空对扶,并且通過isModelSet標(biāo)記判斷l(xiāng)oad
和into的先后順序,我們關(guān)注核心代碼惭缰,buildRequest構(gòu)建了request對象浪南,Request是用來發(fā)出加載圖片請求的,它是Glide中非常關(guān)鍵的一個組件漱受。我們先來看buildRequest()方法是如何構(gòu)建Request對象的:
private Request buildRequest(Target<TranscodeType> target) {
return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(),
requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight());
}
內(nèi)部又調(diào)用了
private Request buildRequestRecursive(Target<TranscodeType> target,
@Nullable ThumbnailRequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority, int overrideWidth, int overrideHeight) {
if (thumbnailBuilder != null) {
// Recursive case: contains a potentially recursive thumbnail request builder.
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a "
+ "thumbnail, consider using clone() on the request(s) passed to thumbnail()");
}
TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
thumbnailBuilder.transitionOptions;
if (DEFAULT_ANIMATION_OPTIONS.equals(thumbTransitionOptions)) {
thumbTransitionOptions = transitionOptions;
}
Priority thumbPriority = thumbnailBuilder.requestOptions.isPrioritySet()
? thumbnailBuilder.requestOptions.getPriority() : getThumbnailPriority(priority);
int thumbOverrideWidth = thumbnailBuilder.requestOptions.getOverrideWidth();
int thumbOverrideHeight = thumbnailBuilder.requestOptions.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !thumbnailBuilder.requestOptions.isValidOverride()) {
thumbOverrideWidth = requestOptions.getOverrideWidth();
thumbOverrideHeight = requestOptions.getOverrideHeight();
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, requestOptions, coordinator,
transitionOptions, priority, overrideWidth, overrideHeight);
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailBuilder.buildRequestRecursive(target, coordinator,
thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, requestOptions, coordinator, transitionOptions,
priority, overrideWidth, overrideHeight);
RequestOptions thumbnailOptions = requestOptions.clone()
.sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest = obtainRequest(target, thumbnailOptions, coordinator,
transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority,
overrideWidth, overrideHeight);
}
}
大篇幅寫的是如何處理縮略圖的络凿,我們關(guān)心這個方法obtainRequest
private Request obtainRequest(Target<TranscodeType> target,
RequestOptions requestOptions, RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority,
int overrideWidth, int overrideHeight) {
requestOptions.lock();
return SingleRequest.obtain(
context,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
requestListener,
requestCoordinator,
context.getEngine(),
transitionOptions.getTransitionFactory());
}
在這里調(diào)用了SingleRequest的obtain方法,這么多參數(shù)昂羡,是不是把之前所有用到的api都添加到這個request中去了
public static <R> SingleRequest<R> obtain(
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
RequestOptions requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> requestListener,
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(
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
requestListener,
requestCoordinator,
engine,
animationFactory);
return request;
}
private void init(
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
RequestOptions requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory) {
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.requestListener = requestListener;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
status = Status.PENDING;
}
new 了一個SingleRequest對象絮记,這個類是Request的實(shí)現(xiàn)類,下邊的init實(shí)際上是對這些參數(shù)賦值虐先,至此解決了創(chuàng)建request的問題怨愤,那么是怎么執(zhí)行的呢
回到最還是into方法
Request request = buildRequest(target);
target.setRequest(request);
requestManager.track(target, request);
看看track方法
void track(Target<?> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
這里涉及到TargetTracker這個類,是對目標(biāo)view的生命周期進(jìn)行跟蹤管理的輔助類
接下來看runRequest這個方法
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
這個RequestTracker類是對request進(jìn)行隊(duì)列管理的類蛹批,當(dāng)請求不是暫停時請求開始撰洗,先不考慮暫停又開始的情況,由于上面分析了SingleRequest是Request的實(shí)現(xiàn)類般眉,那么就去那看
public void begin() {
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
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;
}
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 (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
當(dāng)model==null時也就是load中傳入的為空時我們看這個方法 onLoadFailed
private void onLoadFailed(GlideException e, int maxLogLevel) {
stateVerifier.throwIfRecycled();
int logLevel = glideContext.getLogLevel();
if (logLevel <= maxLogLevel) {
Log.w(GLIDE_TAG, "Load failed for " + model + " with size [" + width + "x" + height + "]", e);
if (logLevel <= Log.INFO) {
e.logRootCauses(GLIDE_TAG);
}
}
loadStatus = null;
status = Status.FAILED;
//TODO: what if this is a thumbnail request?
if (requestListener == null
|| !requestListener.onLoadFailed(e, model, target, isFirstReadyResource())) {
setErrorPlaceholder();
}
}
執(zhí)行了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);
}
下面看一下getFallbackDrawable方法了赵,是干什么的
private Drawable getFallbackDrawable() {
if (fallbackDrawable == null) {
fallbackDrawable = requestOptions.getFallbackDrawable();
if (fallbackDrawable == null && requestOptions.getFallbackId() > 0) {
fallbackDrawable = loadDrawable(requestOptions.getFallbackId());
}
}
return fallbackDrawable;
}
public final Drawable getFallbackDrawable() {
return fallbackDrawable;
}
/**
* Sets an {@link Drawable} to display if the model provided to
* {@link com.bumptech.glide.RequestBuilder#load(Object)} is {@code null}.
*
* <p> If a fallback is not set, null models will cause the error drawable to be displayed. If the
* error drawable is not set, the placeholder will be displayed.
*
* @see #placeholder(Drawable)
* @see #placeholder(int)
*
* @param drawable The drawable to display as a placeholder.
* @return This request builder.
*/
public RequestOptions fallback(Drawable drawable) {
if (isAutoCloneEnabled) {
return clone().fallback(drawable);
}
this.fallbackDrawable = drawable;
fields |= FALLBACK;
return selfOrThrowIfLocked();
}
這里寫的很明白了潜支,當(dāng)load的參數(shù)為空的時展示一個占位圖片甸赃,如果這個圖片沒有設(shè)置,就設(shè)置一個錯誤圖片冗酿,如果錯誤圖片都沒設(shè)置的話只能設(shè)置一個loading圖
下面看看target.onLoadFailed(error);這個方法干了什么:
/**
* A lifecycle callback that is called when a load fails.
*
* <p> Note - This may be called before {@link #onLoadStarted(android.graphics.drawable.Drawable)
* } if the model object is null.
*
* <p>You must ensure that any current Drawable received in {@link #onResourceReady(Object,
* Transition)} is no longer displayed before redrawing the container (usually a View) or
* changing its visibility.
*
* @param errorDrawable The error drawable to optionally show, or null.
*/
void onLoadFailed(@Nullable Drawable errorDrawable);
這是個接口埠对,看看具體實(shí)現(xiàn)
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param errorDrawable {@inheritDoc}
*/
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
這里可以看到,因?yàn)槌霈F(xiàn)了 異常無法正常過的加載圖片裁替,具體的為view設(shè)置了圖片项玛,至于是什么圖片則是給的什么接什么,讓我們回到begin方法();
target.onLoadStarted()圖片請求開始之前弱判,會先使用這張占位圖代替最終的圖片顯示,這也就是placeholder和error的底層實(shí)現(xiàn)原理
我們看看這個方法里邊是什么
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param placeholder {@inheritDoc}
*/
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param drawable {@inheritDoc}
*/
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
知道了占位圖的實(shí)現(xiàn)襟沮,那么 圖片加載是從哪開始的呢,在begin方法里
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
判斷了下你是否指定了一個固定的寬高昌腰,不管你是否設(shè)置了寬高都會走這個方法
/**
* A callback method that should never be invoked directly.
*/
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
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 (Log.isLoggable(TAG, Log.VERBOSE)) {
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.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
從這開始涉及engine我們先看這個類是干嘛的
/**
* Responsible for starting loads and managing active and cached resources.
* 負(fù)責(zé)啟動加載和管理活動和緩存資源开伏。
*/
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
/**
* Starts a load for the given arguments. Must be called on the main thread.
*
* <p> The flow for any request is as follows: <ul> <li>Check the memory cache and provide the
* cached resource if present</li> <li>Check the current put of actively used resources and return
* the active resource if present</li> <li>Check the current put of in progress loads and add the
* cb to the in progress load if present</li> <li>Start a new load</li> </ul> </p>
*
* <p> Active resources are those that have been provided to at least one request and have not yet
* been released. Once all consumers of a resource have released that resource, the resource then
* goes to cache. If the resource is ever returned to a new consumer from cache, it is re-added to
* the active resources. If the resource is evicted from the cache, its resources are recycled and
* re-used if possible and the resource is discarded. There is no strict requirement that
* consumers release their resources so active resources are held weakly. </p>
*
* @param width The target width in pixels of the desired resource.
* @param height The target height in pixels of the desired resource.
* @param cb The callback that will be called when the load completes.
*/
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,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
//EngineKey 的介紹是An in memory only cache key used to multiplex loads.
//內(nèi)存中只緩存用于復(fù)用的鍵。所以這個key記錄了一次加載的各種信息
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//這個應(yīng)該也屬于內(nèi)存緩存遭商,這里避免了EngineJob的重復(fù)創(chuàng)建固灵,EngineJob代表從硬盤緩存或者網(wǎng)絡(luò)上加載圖片并進(jìn)行decode的整個過程。
EngineJob<?> current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
useUnlimitedSourceExecutorPool);
DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
這個方法有點(diǎn)長劫流,我們慢慢看
從這就開始涉及Glide的緩存機(jī)制
EngineKey 這個類是用產(chǎn)生一個用于進(jìn)行緩存的Key巫玻,決定key的因素特別多丛忆,這個類通過重寫了equals()和hashCode()方法,保證只有傳入EngineKey的所有參數(shù)都相同的情況下才認(rèn)為是同一個EngineKey對象
接下來看loadFromCache方法
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /*isMemoryCacheable*/);
}
return result;
}
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = null;
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}
為什么要判斷isMemoryCacheable 呢是因?yàn)镚lide自動幫我們開啟了緩存如果想關(guān)閉這功能只需要在skipMemoryCache()方法傳入true仍秤,那么在這里就是false熄诡,表示內(nèi)存被禁用,接著調(diào)用了getEngineResourceFromCache()方法來獲取緩存诗力。在這個方法中粮彤,會使用緩存Key來從cache當(dāng)中取值,而這里的cache對象就是在構(gòu)建Glide對象時創(chuàng)建的LruResourceCache姜骡,那么說明這里其實(shí)使用的就是LruCache算法了导坟,當(dāng)我們從LruResourceCache中獲取到緩存圖片之后會將它從緩存中移除,然后在將這個緩存圖片存儲到activeResources當(dāng)中圈澈。activeResources就是一個弱引用的HashMap惫周,用來緩存正在使用中的圖片,我們可以看到康栈,loadFromActiveResources()方法就是從activeResources這個HashMap當(dāng)中取值的递递。使用activeResources來緩存正在使用中的圖片,可以保護(hù)這些圖片不會被LruCache算法回收掉啥么,如果有的話就直接在這獲取登舞,沒有的話就開啟線程去加載圖片,接下來又進(jìn)行了內(nèi)存緩存的判斷這里避免了EngineJob的重復(fù)創(chuàng)建,EngineJob代表從硬盤緩存或者網(wǎng)絡(luò)上加載圖片并進(jìn)行decode的整個過程悬荣。
那么怎么開啟的線程又是如何加載的呢:
EngineJob的職責(zé)是調(diào)度DecodeJob菠秒,添加,移除資源回調(diào)氯迂,并notify回調(diào)践叠。DecodeJob負(fù)責(zé)從緩存資源或者原始數(shù)據(jù)中讀取資源,Glide中的臟累活基本都是這個DecodeJob干的
先從engineJob.start去看
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
利用線程池執(zhí)行decodeJob嚼蚀,那么decodeJob又有什么貓膩
decodeJob中實(shí)現(xiàn)了Runnable看一下run方法
@Override
public void run() {
// This should be much more fine grained, but since Java's thread pool implementation silently
// swallows all otherwise fatal exceptions, this will at least make it obvious to developers
// that something is failing.
TraceCompat.beginSection("DecodeJob#run");
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (RuntimeException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, e);
}
// When we're encoding we've already notified our callback and it isn't safe to do so again.
if (stage != Stage.ENCODE) {
notifyFailed();
}
if (!isCancelled) {
throw e;
}
} finally {
if (currentFetcher != null) {
currentFetcher.cleanup();
}
TraceCompat.endSection();
}
}
貌似所有的邏輯處理都在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);
}
}
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);
}
}
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;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
因?yàn)閞unReason的默認(rèn)值是INITIALIZE禁灼,首先調(diào)用getNextStage方法獲取到stage值,然后執(zhí)行g(shù)etNextGenerator根據(jù)stage不同的值獲取相應(yīng)的generator
一共有幾種generators
ResourceCacheGenerator:從處理過的緩存加載數(shù)據(jù)
DataCacheGenerator:從原始緩存加載數(shù)據(jù)
SourceGenerator:從數(shù)據(jù)源請求數(shù)據(jù)
最后執(zhí)行runGeneratores轿曙,currentGenerator.startNext()
點(diǎn)進(jìn)去看到時候發(fā)現(xiàn)有3個
分別是:
DataCacheGenerator
ResourceCacheGenerator
SourceGenerator
這里主要看一下SourceGenerator的方法
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
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;
}
接下來我們看loadData方法
/**
* A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
*/
public class HttpUrlFetcher implements DataFetcher<InputStream> {
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
final InputStream result;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
glideUrl.getHeaders());
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
return;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
+ " ms and loaded " + result);
}
callback.onDataReady(result);
}
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();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == -1) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
)
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;
}
到這我們是找到了網(wǎng)絡(luò)通訊的代碼了弄捕,通過網(wǎng)絡(luò)下載獲取到inputStream,然后把流給callback.onDataReady(result);
我們再看代碼:SourceGenerator
@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);
}
}
最后回調(diào)到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 {
TraceCompat.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
TraceCompat.endSection();
}
}
}
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 {
//把數(shù)據(jù)流編碼成resource類型
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
exceptions.add(e);
}
if (resource != null) {
//將resource返回
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
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;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
onEncodeComplete();
}
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
最終把數(shù)據(jù)回調(diào)到onResourceReady
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
之后發(fā)送了一條消息
private static class MainThreadCallback implements Handler.Callback {
@Synthetic
MainThreadCallback() { }
@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;
}
}
@Synthetic
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release(false /*isRemovedFromQueue*/);
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);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
之后回調(diào)到SingleRequest
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception = new GlideException("Expected to receive a Resource<R> with an "
+ "object of " + transcodeClass + " inside, but instead got null.");
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
GlideException exception = new GlideException("Expected to receive an object of "
+ transcodeClass + " but instead" + " got "
+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
+ "Resource{" + resource + "}."
+ (received != null ? "" : " " + "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
+ LogTime.getElapsedMillis(startTime) + " ms");
}
if (requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource)) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
}
最后我們看 target.onResourceReady(result, animation);這個方法
點(diǎn)進(jìn)去一看
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
@Override
public void onResourceReady(Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
}
private void setResourceInternal(@Nullable Z resource) {
maybeUpdateAnimatable(resource);
setResource(resource);
}
protected abstract void setResource(@Nullable Z resource);
至此就能把圖片顯示出來了导帝,至于如何編碼解碼的并沒有深入探討分析守谓,整體的過了一遍流程
總體來說跟3.7.0流程上還是有很大的區(qū)別的,至于沒弄的細(xì)節(jié)有時間再弄舟扎。