Glide源碼解析-載入流程
1 引言
一直想要閱讀Glide源碼,但是苦於時間和功力都不夠,總是斷斷續續的,趁著現在有一些空暇時間,來簡要分析Glide的源碼。Glide的實現太過複雜,不可能做到面面俱到,如果每一行都細緻分析,很容易陷入作者的優化細節中去而偏離主線,因此只針對幾個主要功能做解析即可。 以下分析全部基於Glide v4.9.0。
2 初始化
Glide最常見的用法就是如下一行代碼:
Glide.with(context).load(url).into(imageView);
一步步來分析如何將url圖片載入到imageview上來。
2.1 with
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
with方法是Glide類中的一組同名static重載函數,可以傳入多種上下文,方法體內是調用getRetriever
獲得RequestManagerRetriever
實例對象,再調用其get方法返回一個RequestManager
實例。
private static volatile Glide glide;
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.
...
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
這裡Glide的get方法用了DCL單例,然後拿到Glide的成員變數requestManagerRetriever。 然後再看RequestManagerRetriever類。
@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);
}
這是上文with方法里調用的get方法,這裡會對傳入的context做判斷,如果方法調用是在主線程同時context不是Application,則會根據context的類型調用一組重載的get方法,否則就調用getApplicationManager。
那這兩個分支有什麼區別呢?具體看一下兩個處理方法。
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or were 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;
}
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));
}
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
這裡的區別主要是將glide和不同的上下文的聲明周期綁定,如果是Application或者不在主線程調用,那requetmanager的生命周期和Application相關,否則則會和當前頁面的fragmentManager的聲明周期相關。因為Activity下fragmentManager的生命周期和Activity相同。所以不管是Activity還是fragment,最後都會委託給fragmentManager做生命周期的管理。
在getSupportRequestManagerFragment方法中可以看到如果activity下的fragmentmanager沒有找到tag為FRAGMENT_TAG的fragment,就會創建一個隱藏的fragment,然後添加到fragmentmanager內。
總結來說with方法的作用就是獲得當前上下文,構造出和上下文生命周期綁定的requestmanager,自動管理glide的載入開始和停止。
2.2 load
load方法也是一組重載方法,定義在interface ModelTypes<T>
介面里,這是一個泛型介面,規定了load想要返回的數據類型,RequestManager
類實現了該介面,泛型為Drawable類。
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
......
RequestManager下的load方法都返回RequestBuilder對象,顯然是一個建造者模式,用來構建需要的屬性。asDrawable方法調用的as方法實際上是調用RequestBuilder的構造方法。然後掉用RequestBuilder的load將需要載入的object傳遞給builder,然後load方法都會調用loadGeneric將不同的參數類型統一傳給Object類的model成員變數,如果傳遞的參數類型是Drawable或者Bitmap,那麼將會額外調用.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
,意味著這兩種類型將不會做緩存。
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
總結一下load作用,構造一個RequestBuilder實例,同時傳入需要載入的數據源類型。
2.3 into
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
//檢查是否額外設置了imageview的裁剪方法
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
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(
//構建target
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
into方法同樣是在RequestBuilder內,是glide載入圖片流程的最後一步,他暴露了兩種public方法,一個的參數是imageview,作用是指定圖片最後要載入到的位置。另一個參數是target對象,可以定製化一個target並返回。
對於參數imageview的into方法,首先先檢查requestBuilder是否額外設置過imageview的scaletype屬性,如果有則在requestoption裡面加上裁剪選項,接著構建一個target實例並創建一個主線程的executor用於獲得圖片資源在主線程更新UI,調用private的into方法。
創建target方法細節如下:
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
//工廠方法創建target的子類viewtarget
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
//根據傳入的不同class類型構造bitmap或者drawabletarget
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
/**
* @deprecated Use {@link #waitForLayout()} instead.
*/
// Public API.
@SuppressWarnings({"unused", "deprecation"})
@Deprecated
public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
//負責將最後獲得Drawable資源載入到into指定的imageview上
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
對於參數target的into方法,則是直接創建一個主線程的runnable用於回調target給主線程。
接下來看private的into方法做了什麼:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
//判斷是否調用過load方法設置目資源
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//構建Request實例
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
//使用上一個請求而不是新請求來允許優化,例如跳過設置佔位符,跟蹤和取消跟蹤目標,以及獲取在單個請求中完成的查看維度。
previous.begin();
}
return target;
}
//取消掛起的任務 清除資源 達到復用目的
requestManager.clear(target);
//為target綁定request
target.setRequest(request);
//執行request
requestManager.track(target, request);
return target;
}
先判斷是否調用過load方法設置目標資源變數,如果沒有直接拋出異常,接著構建request實例,同時獲得target上的前一個request(參數為imageview的into方法跳過),如果相同則直接復用前一個request,免去了一些配置步驟,同時為了能順利完成回調,增加了重試機制。然後對於imageview來說會先取消之前掛起的任務清楚任務資源,然後為target重新綁定request請求,track方法開始執行request任務。
看一下最後track方法做了什麼:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
//將target加入到追蹤隊列
targetTracker.track(target);
//執行request請求
requestTracker.runRequest(request);
}
首先會將目標target加入到追蹤隊列,這個隊列里保存了當前activity里所有的target,同時和生命周期進行了綁定,這樣做的好處是用生命周期自動管理了request請求的開始、暫停、結束等操作。
public final class TargetTracker implements LifecycleListener {
private final Set<Target<?>> targets =
Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
public void untrack(@NonNull Target<?> target) {
targets.remove(target);
}
@Override
public void onStart() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStart();
}
}
@Override
public void onStop() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStop();
}
}
@Override
public void onDestroy() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onDestroy();
}
}
@NonNull
public List<Target<?>> getAll() {
return Util.getSnapshot(targets);
}
public void clear() {
targets.clear();
}
}
runRequest方法則是最終真正執行載入圖片資源的操作:
public void runRequest(@NonNull Request request) {
requests.add(request);
//request隊列是否處於暫停狀態
if (!isPaused) {
//如果不則執行request
request.begin();
} else {
//清楚request資源
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
//加入掛起隊列
pendingRequests.add(request);
}
}
begin操作是調用了SingleRequest類的begin方法,SingleRequest實現了Request介面:
public synchronized 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;
}
//正在運行 重複執行Request拋出異常
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
//如果我們在完成之後重新啟動(通常通過諸如notifyDataSetChanged之類的東西,在相同的目標或視圖中啟動相同的請求),我們可以簡單地使用我們上次檢索的資源和大小,並跳過獲取新的大小
//這意味著想要重新啟動負載因為期望視圖大小已更改的用戶需要在開始新載入之前明確清除視圖或目標。
if (status == Status.COMPLETE) {
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());
}
}
方法里首先判斷是否設置過model,如果沒有則直接回調載入失敗,然後判斷是否正在運行,如果重複請求就直接拋出異常,接著判斷是否Request已經完成,完成則調用onResourceReady
,接著給view確定height和width,同時調用onSizeReady
,如果狀態處於running或者WAITING_FOR_SIZE,調用onLoadStarted
。
這三個回調的名字很明顯,分別對應Request的三個過程,onSizeReady(準備)、onLoadStarted(開始)、onResourceReady(資源完成),一個個來看。
2.3.1 onSizeReady
public synchronized void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
//如果進入時不是WAITING_FOR_SIZE直接退出
if (status != Status.WAITING_FOR_SIZE) {
return;
}
//狀態調整至running
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
if (status != Status.RUNNING) {
loadStatus = null;
}
}
load方法就是真正執行載入資源的代碼,裡面有一個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 enum Stage {
/** The initial stage. */
INITIALIZE,
/** Decode from a cached resource. */
RESOURCE_CACHE,
/** Decode from cached source data. */
DATA_CACHE,
/** Decode from retrieved source. */
SOURCE,
/** Encoding transformed resources after a successful load. */
ENCODE,
/** No more viable stages. */
FINISHED,
}
這裡做了一個狀態機的轉換,按照Stage的流程不斷的流轉,如果runReason是INITIALIZE,就獲取Stage.INITIALIZE的下一個狀態,先從RESOURCE_CACHE內存里獲取緩存,再從DATA_CACHE磁碟獲取緩存,再從source數據源取數據。這就是三級緩存的策略。
三級緩存的生成對應著三個生成類,通過調用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);
}
}
獲取到DataFetcherGenerator之後,就會調用runGenerators方法去執行獲取數據操作,startNext方法就是內部獲取數據代碼。
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;
}
}
// Weve run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
來看一下SourceGenerator的startNext(),會調用loadData方法,根據不同的獲取資源策略載入數據,在HttpUrlFetcher類里也就是網路請求數據的loadData方法中,會請求url拿到輸入流,然後回調給Generator,Generator的onDataReady方法接收到回調之後會根據緩存策略選擇將數據緩存起來或是回調數據給外部。
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;
}
//HttpUrlFetcher.java
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));
}
}
}
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 elses thread. Before doing anything, we should
// reschedule to get back onto Glides thread.
cb.reschedule();
} else {
//不能緩存就仍舊向外回調數據
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
2.3.2 onLoadStarted
當status的狀態為running或者WAITING_FOR_SIZE的時候,就會調用該方法,它會調用target的onLoadStarted做一些準備工作,在ImageViewTarget
類中就會設置placeholder和一些載入動畫。
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
2.3.3 onResourceReady
這個方法就是最後將獲得數據裝進imageview或者返回給target的方法:
private synchronized 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");
}
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
// 1
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
重點看一下注釋1出的target.onResourceReady,它將獲取到的數據通過target調用塞入target中進行載入,看一下ImageViewTarget的方法:
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
//沒有變化 則直接setresource
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
//有變換 需要更新動畫
maybeUpdateAnimatable(resource);
}
}
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 void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
3 總結
上文的解析,一套完整的流程就走下來了,但是glide強大之處遠不止於此,還有具體的緩存策略沒有講解,後續會繼續對glide進行分析。
4 參考資料
Android圖片載入框架最全解析(二),從源碼的角度理解Glide的執行流程
Glide github
推薦閱讀:
TAG:Android開發 |