之前對Glide的認知一直停留在一行代碼就能夠完成圖片加載,如今就來嘗試探索下這一行代碼下,Glide到底作了些什麼。本文基於Glide4.8.0java
以加載一張普通的網絡圖片爲例緩存
val jpg = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg"
Glide.with(this)
.load(img)
.into(iv)
複製代碼
就這麼核心的一行代碼一張圖片就會加載到iv中去了,下面從with開始分析下它的源碼bash
with方法提供了不少方法重載,入參都是一些能直接或間接取到Context實例的類型,其基本流程以下網絡
下面根據這個流程來看看源碼app
// Glide.java
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
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;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
initializeGlide(context);
}
private static void initializeGlide(@NonNull Context context) {
initializeGlide(context, new GlideBuilder());
}
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
RequestManagerRetriever.RequestManagerFactory factory = null;
builder.setRequestManagerFactory(factory);
Glide glide = builder.build(applicationContext);
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
複製代碼
經過源碼發現getRetriever方法內部其實就是建立了Glide實例,而且調用其getRequestManagerRetriever,先來看看Glide實例的構建過程ide
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(...);
}
複製代碼
能夠看到內部先是構建了三個線程池,而後新建了一個Engine類實例,建立了RequestManagerRetriever實例,該實例就是調用getRequestManagerRetriever獲取到的實例。接着看看Glide的構造方法。oop
Glide(...) {
registry = new Registry();
// 這裏註冊了不少東西,好比編碼器、解碼器、工廠類、轉換器。這裏只是挑了幾個
registry
// 註冊Encoder
.append(ByteBuffer.class, new ByteBufferEncoder())
// 註冊Decoder
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
// 註冊ModelLoaderFactory,三個參數分別是Model類型,Data類型,ModelLoader工廠
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
// 註冊Transcoders
.register(Bitmap.class,BitmapDrawable.class,new BitmapDrawableTranscoder(resources))
...
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext = new GlideContext(...);
}
複製代碼
獲取到了RequestManagerRetriever實例後繼續調用其get方法fetch
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));
}
}
複製代碼
這裏先分析主線程調用的狀況,所以接着調用supportFragmentGetui
private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
// 首先看看當前頁面是否已經有指定Fragment了,若是沒有那麼就新建一個而且加入到當前頁面中去
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;
}
複製代碼
能夠看到supportFragmentGet首先將一個Fragment添加到當前頁面(用於觀察Activity的生命週期),而後建立一個RequestManager實例並返回,with方法結束而後看看load方法this
load方法位於RequestManager中用於將指定資源做爲數據源這裏以參數爲String爲例
下面根據這個流程來看看源碼
// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
複製代碼
asDrawable內部只是建立了一個RequestBuilder實例,接着再調用其load方法
// RequestBuilder.java
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
複製代碼
load方法內部也就是進行了簡單賦值就將本身返回了,至此load方法也已經結束了,接着看看重點into方法
into方法一樣定義在RequestBuilder中,主要用於將加載到的圖片傳遞給指定Target實例,這個方法內部至關複雜,也是本文的重點。
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
RequestOptions requestOptions = this.requestOptions;
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:
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
複製代碼
這裏會對RequestOptions作一些轉化,而後調用buildImageViewTarget構造指定的target。
// GlideContext.java
public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
// ImageViewTargetFactory.java
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
複製代碼
因爲剛纔調用了asDrawable因此會新建一個DrawableImageViewTarget實例返回,接着繼續看看into方法
private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, @NonNull RequestOptions options) {
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
複製代碼
into方法內部首先判斷了是否調用了load方法而後調用buildRequest建立Request實例,而後調用track方法執行Request
private Request buildRequest( Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, RequestOptions requestOptions) {
return buildRequestRecursive(...);
}
private Request buildRequestRecursive(...) {
Request mainRequest =
buildThumbnailRequestRecursive(...);
if (errorRequestCoordinator == null) {
return mainRequest;
}
Request errorRequest = errorBuilder.buildRequestRecursive(...);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
private Request buildThumbnailRequestRecursive(...) {
// 只考慮沒有縮略圖的狀況了
return obtainRequest(...);
}
private Request obtainRequest(...) {
return SingleRequest.obtain(...);
}
複製代碼
能夠看到當沒有縮略圖的時候會經過調用SingleRequest.obtain新建一個SingleRequest實例,接着看看requestManager.track是怎麼執行Request的
// RequestManager.java
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
pendingRequests.add(request);
}
}
複製代碼
若是沒有被暫定的話就接着繼續執行SingleRequest的begin方法
// SingleRequest.java
public void begin() {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
}
複製代碼
根據是否設置了寬高來決定是調用onSizeReady仍是target.getSize,先假設沒有設置寬高來看看DrawableImageViewTarget實例的getSize方法
// getSize繼承自ViewTarget
public void getSize(@NonNull SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
// SizeDeterminer.java
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
private int getTargetWidth() {
int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
LayoutParams layoutParams = view.getLayoutParams();
int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
}
private int getTargetHeight() {
int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
LayoutParams layoutParams = view.getLayoutParams();
int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
}
private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
int adjustedParamSize = paramSize - paddingSize;
if (adjustedParamSize > 0) {
return adjustedParamSize;
}
if (waitForLayout && view.isLayoutRequested()) {
return PENDING_SIZE;
}
int adjustedViewSize = viewSize - paddingSize;
if (adjustedViewSize > 0) {
return adjustedViewSize;
}
if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
return getMaxDisplayLength(view.getContext());
}
return PENDING_SIZE;
}
複製代碼
getSize內部經過獲取View的長寬減去對應的padding作爲目標寬高,而後又回調了onSizeReady方法,注意若是在此時目標ImageView尚未進行三大流程(也就是說獲取不到寬高)那麼會註冊回調,在回調中會調用onSizeReady方法這裏就不展開了,所以不論是否指定了寬高最終都會調用SingleRequest的onSizeReady方法
// SingleRequest.java
public void onSizeReady(int width, int height) {
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
loadStatus = engine.load(...);
}
複製代碼
onSizeReady內部就分爲三步首先改變狀態爲Running而後計算最終須要加載的寬高,最後調用engine.load加載資源
public <R> LoadStatus load(...) {
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(...);
DecodeJob<R> decodeJob =
decodeJobFactory.build(...);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
複製代碼
load方法首先構建一個EngineKey實例,內部只是重寫了hashCode和equal方法,接着若是容許讀取內存緩存就嘗試從ActivityResources中讀取讀不到再從MemoryCache中讀取,Tip: 暫時沒分清這二者的區別。若是已經有了當前key對應的EngineJob就直接返回,接着建立一個EngineJob和一個DecodeJob實例,而後調用engineJob.start
// EngineJob.java
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
// DecodeJob.java
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
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:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
複製代碼
首先根據是否從緩存中解碼選取線程池,接着使用線程池執行DecodeJob,而若是沒有設置DiskCacheStrategy那麼默認的DiskCacheStarategy.Automatic的decodeCachedResource返回true,因此willDecodeFromCache會返回true,因此最終executor會是diskCacheExecutor,而這個線程池就是在構建Glide實例的時候建立的diskCacheExecutor,接着看看DecodeJob的run方法
// DecodeJob.java
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
...
}
}
複製代碼
若是取消了,就通知下失敗,不然主要就是調用了runWrapped
// DecodeJob.java
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);
}
}
複製代碼
因爲DecodeJob實例剛剛建立因此runReason爲構造器時初始化的RunReason.INITIALIZE狀態,getNextStage返回Stage.RESOURCE_CACHE,getNextGenerator返回一個ResourceCacheGenerator實例,接着看看runGenerator方法
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;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
複製代碼
內部調用了startNext方法,並根據其返回值判斷是否進入下一步,先來看看ResourceCacheGenerator的startNext方法
// ResourceCacheGenerator.java
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
}
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
// 獲取GlideUrl
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
currentKey =
new ResourceCacheKey(...);
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
複製代碼
getCacheKey這個方法內部很是複雜,一步步來進行分析
// DecoderHelp.java
List<Key> getCacheKeys() {
if (!isCacheKeysSet) {
isCacheKeysSet = true;
cacheKeys.clear();
List<LoadData<?>> loadData = getLoadData();
for (int i = 0, size = loadData.size(); i < size; i++) {
LoadData<?> data = loadData.get(i);
if (!cacheKeys.contains(data.sourceKey)) {
cacheKeys.add(data.sourceKey);
}
for (int j = 0; j < data.alternateKeys.size(); j++) {
if (!cacheKeys.contains(data.alternateKeys.get(j))) {
cacheKeys.add(data.alternateKeys.get(j));
}
}
}
}
return cacheKeys;
}
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
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;
}
// Registry.java
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(
@NonNull Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}
// MultiModelLoaderFactory.java
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
// 這個地方可能會觸發遞歸,爲了防止棧溢出,用過的entry不容許再用
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
複製代碼
getCacheKeys內部的一個getLoadData的代碼就很長,通過層層調用最終會調用到build方法從Glide實例一建立就註冊的的全部Factory中找尋到全部能處理Model類型爲String的Factory,也就是以下四個
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
複製代碼
這四個來一個個進行分析(爲何要一個個分析接着看就知道了),首先會調用到DataUrlLoader.StreamFactory的build方法
// DataUrlLoader.StreamFactory.java
public ModelLoader<Model, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
return new DataUrlLoader<>(opener);
}
複製代碼
內部簡單的建立了一個DataUrlLoader實例,而後將其放入到了loaders中。
接着看看第二個StringLoader.StreamFactory的build方法
// StringLoader.StreamFactory.java
public ModelLoader<String, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
複製代碼
此次跟DataUrlLoader.StreamFactory不同了,內部建立了一個StringLoader實例,可是又回調了MultiModelLoaderFactory兩個參數的build方法,而且明確指定了要尋找能將Model類型爲Uri的處理成Data類型爲InputStream的Factory,先來看看兩個參數的build方法
// MultiModelLoaderFactory.java
public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass, Class<DatdataClass) {
try {
List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
boolean ignoredAnyEntries = false;
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {
ignoredAnyEntries = true;
continue;
}
if (entry.handles(modelClass, dataClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Data>build(entry));
alreadyUsedEntries.remove(entry);
}
}
if (loaders.size() > 1) {
return factory.build(loaders, throwableListPool);
} else if (loaders.size() == 1) {
return loaders.get(0);
} else {
if (ignoredAnyEntries) {
return emptyModelLoader();
} else {
throw new NoModelLoaderAvailableException(modelClass, dataClass);
}
}
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
複製代碼
代碼基本與一個參數的build方法相似,不一樣點在於明確指定了目標Data類型和返回類型,那麼以前在註冊列表中尋找Model類型爲Uri,Data類型爲InputStream的factory,以下所示
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
複製代碼
有7個Factory知足條件,那麼依次再來看這7個Factory的build方法
// DataUrlLoader.StreamFactory.java
public ModelLoader<Model, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
return new DataUrlLoader<>(opener);
}
複製代碼
這個沒什麼特別的接着看下一個
// HttpUriLoader.Factory.java
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
複製代碼
構建HttpUrlLoader的時候又要去尋找能將Model類型爲GlideUrl處理成Data類型爲InputStream的Factory,那麼接着看,經過查詢註冊,只發現瞭如下一個。
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
複製代碼
那麼直接看看其build方法
// HttpGlideUrlLoader.Factory.java
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpGlideUrlLoader(modelCache);
}
複製代碼
只是建立了一個HttpGlideUrlLoader實例就返回(這個實例纔是真正的去請求網絡數據),接着看其他5個能將Model(Uri)處理成Data(InputStream)的Factory
// AssetUriLoader.StreamFactory.java
public DataFetcher<InputStream> buildFetcher(AssetManager assetManager, String assetPath) {
return new StreamAssetPathFetcher(assetManager, assetPath);
}
複製代碼
方法內部僅僅建立了一個StreamAssetPathFetcher實例接着看看下一個
// MediaStoreImageThumbLoader.Factory
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new MediaStoreImageThumbLoader(context);
}
複製代碼
方法內部僅僅建立了一個MediaStoreImageThumbLoader實例接着看看下一個
// MediaStoreVideoThumbLoader.java
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new MediaStoreVideoThumbLoader(context);
}
複製代碼
方法內部僅僅建立了一個MediaStoreVideoThumbLoader實例接着看看下一個
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new UriLoader<>(this);
}
複製代碼
方法內部僅僅建立了一個UriLoader實例接着看看最後一個
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new UrlUriLoader<>(multiFactory.build(GlideUrl.class, InputStream.class));
}
複製代碼
內部又尋找能將GlideUrl處理成InputStream的Factory,通過上面的分析也就只有HttpGlideUrlLoader.Factory能進行處理這裏進再也不展開。
接着看能將Model(String)處理成Data(Any)的第三個Factory(StringLoader.FileDescriptorFactory)
// StringLoader.FileDescriptorFactory
public ModelLoader<String, ParcelFileDescriptor> build( @NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
}
複製代碼
內部再去尋找能將Uri處理成ParcelFileDescriptor的Factory,經過查詢有如下兩個
.append(Uri.class,ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(contentResolver))
複製代碼
再來分別看看
// AssetUriLoader.FileDescriptorFactory.java
public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
return new AssetUriLoader<>(assetManager, this);
}
複製代碼
內部僅僅建立了AssetUriLoader,接着看下一個
// UriLoader.FileDescriptorFactory.java
public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
return new UriLoader<>(this);
}
複製代碼
接着看能將Model(String)處理成Data(Any)的最後一個Factory(StringLoader.AssetFileDescriptorFactory)
// StringLoader.AssetFileDescriptorFactory.java
public ModelLoader<String, AssetFileDescriptor> build( @NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, AssetFileDescriptor.class));
}
複製代碼
內部又去尋找能將Uri處理成AssetFileDescriptor的Factory,經過查詢就找到了下面這一個
.append(Uri.class, AssetFileDescriptor.class, new UriLoader.AssetFileDescriptorFactory(contentResolver))
複製代碼
// UriLoad.AssetFileDescriptorFactory.java
public ModelLoader<Uri, AssetFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
return new UriLoader<>(this);
}
複製代碼
而後調用入參爲Entry的build方法,內部調用了entry.factory.build(this),也就是分別調用上述4個Factory的build方法。到如今爲止也就剛剛分析好了MultiModelLoaderFactory.build(String.class)這麼一個方法調用,先來總結下。
能夠看到Glide會尋找全部能處理Model類型String的Factory調用其build方建立ModelLoader,可是有些ModelLoader單獨也不能完成一個轉換,所以這些ModelLoader一般會有另外一個能幫助其完成轉換的ModelLoader實例,來舉個例子,StringLoader沒辦法將Model(String)處理成Data(InputSteam),所以其持有了一個能將Model(Uri)處理能Data(InputStream)的ModelLoad,StringLoader主要功能就是在外界調用buildLoadData
時將String類型的Url轉換爲Uri而後調用那個持有的ModelLoader的buildLoadData
方法,最終可能的轉化路線以下所示
接着再回到getModelLoaders裏面執行剩餘代碼
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
// DataUrlLoader.java
public boolean handles(@NonNull Model model) {
return model.toString().startsWith(DATA_SCHEME_IMAGE);
}
複製代碼
MultiModelLoaderFactory.build只是從全部Factory中找尋出全部能處理Model(String)的Factory,可是有些ModelLoader雖然說也能處理Model(String),可是擁有其侷限性好比DataUrlLoader.StreamFactory.build產生的DataUrlLoader其只能處理以data:image開頭的字符串,所以當該方法返回時還餘下3個ModelLoader,接着再回到DecodeHelp.getLoadData中
// DecodeHelper.java
List<LoadData<?>> getLoadData() {
...
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
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;
}
複製代碼
內部分別調用了三個StringLoader的buildLoadData
來構建LoadData實例
public LoadData<Data> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
複製代碼
能夠看到首先將傳入的String類型的url轉化爲Uri實例,而後調用uriLoader.buildLoadData構建LoadData,一個典型的代理模式,通過前面的分析uriLoader實際上是一個MutiModelLoader(內部有7個ModelLoader,這7個都能將Model(Uri)處理成Data(InputStream))
// MultiModelLoader.java
public LoadData<Data> buildLoadData(@NonNull Model model, int width, int height, @NonNull Options options) {
Key sourceKey = null;
int size = modelLoaders.size();
List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
if (modelLoader.handles(model)) {
LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
if (loadData != null) {
sourceKey = loadData.sourceKey;
fetchers.add(loadData.fetcher);
}
}
}
return !fetchers.isEmpty() && sourceKey != null
? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)) : null;
}
複製代碼
首先第一個DataUrlLoader因爲只能處理data:image開頭的字符串,因此留下6個,來看看第二個HttpUriLoader
public LoadData<InputStream> buildLoadData(@NonNull Uri model, int width, int height, @NonNull Options options) {
return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
}
複製代碼
哎,又跑去調用urlLoader.buildLoadData一層層真深,這個urlLoader仍是包含了HttpGlideUrlLoader對應於前面講的2.2.2.1
// HttpGlideUrlLoader.java
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
複製代碼
千辛萬苦找到了返回的第一個LoadData,注意內部傳入了一個HttpUrlFetcher,後面會用於網絡請求,繼續第三個AssetUriLoader
// AssetUriLoader.java
public boolean handles(@NonNull Uri model) {
return ContentResolver.SCHEME_FILE.equals(model.getScheme()) && !model.getPathSegments()
.isEmpty() && ASSET_PATH_SEGMENT.equals(model.getPathSegments().get(0));
}
複製代碼
很明顯model的scheme是http因此AssetUriLoader不知足,相似的後面三個MediaStoreImageThumbLoader、MediaStoreVideoThumbLoader、UriLoader也無法處理,直接看最後一個UrlUriLoader
// UrlUriLoader.java
private static final Set<String> SCHEMES = Collections.unmodifiableSet(
new HashSet<>(
Arrays.asList(
"http",
"https"
)
)
);
public boolean handles(@NonNull Uri uri) {
return SCHEMES.contains(uri.getScheme());
}
複製代碼
很明顯能夠處理,繼續看其buildLoadData方法
public LoadData<Data> buildLoadData(@NonNull Uri uri, int width, int height, @NonNull Options options) {
GlideUrl glideUrl = new GlideUrl(uri.toString());
return urlLoader.buildLoadData(glideUrl, width, height, options);
}
複製代碼
構建了一個GlideUrl實例,這裏的urlLoader是一個HttpGlideUrlLoader實例對應於2.2.7,這個結果與第二個HttpUriLoader處理結果一致,第一個StringLoader到此結束,一共返回兩個DataFetcher,都是HttpUrlFetcher,封裝成一個LoadData(MultiFetcher)返回,後面的第2、第三個ModelLoader都不符合條件,因此最後getLoadData只會返回一個LoadData,那麼getCacheKeys也就只會返回擁有一個Key的列表,繼續回到ResourceCacheGenerator.startNext,方法後面根據全部註冊的decoder、modelLoader、transCodeClass查詢到全部能夠將Data轉化爲對應Resource的類,最後因爲沒有緩存startNext最終會返回false,DataCacheGenerator也是一樣的道理,最後走到SourceGenerator中
// SourceGenerator.java
public boolean startNext() {
...
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;
}
複製代碼
注意這裏helper.getLoadData()獲取到的就是剛開始ResourceCacheGenerator調用getLoadData保存在其中的一個列表,接着會調用fetcher.loadData,前面已經分析過fetcher就是MultiFetcher,內部擁有兩個HttpUrlFetcher
// MultiFetcher.java
public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
this.priority = priority;
this.callback = callback;
exceptions = throwableListPool.acquire();
fetchers.get(currentIndex).loadData(priority, this);
}
複製代碼
這裏只會啓動第一個HttpUrlFetcher,第二個只會在第一個失敗的時候纔會調用,繼續看看HttpUrlFetcher.loadData
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) {
callback.onLoadFailed(e);
}
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
...
urlConnection = connectionFactory.build(url);
...
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
...
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
}
...
}
複製代碼
內部也就是直接使用了HttpUrlConnection,而且支持重定向(最多5次),若是請求成功了則會返回一個InputStream實例,而且回調callback.onDataReady,這裏的callback就是MultiFetcher
// MultiFetcher.java
public void onDataReady(@Nullable Data data) {
if (data != null) {
callback.onDataReady(data);
} else {
startNextOrFail();
}
}
複製代碼
若是加載成功了那麼再回調onDataReady,否則就使用下一個Fetcher請求數據或者只會返回錯誤,這裏只看正返回成功的狀況,callback是SourceGenerator
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
複製代碼
默認的DiskCacheStrategy是AUTOMATIC,因此傳入的REMOTE知足第一個If條件,cb是DecodeJob實例
// DecodeJob.java
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
public void reschedule(DecodeJob<?> job) {
getActiveSourceExecutor().execute(job);
}
複製代碼
接着又會執行這個DecodeJob,不過此次與第一次不一樣,此次調用是DecodeJob不是新建的其成員都維持着 執行順序 DecodeJob.run -> runWrapped -> runGenerators -> SourceGenerator.startNext
// SourceGenerator.java
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
}
複製代碼
此次dataToCache不是null了而是一個InputStream實例
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
...
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
複製代碼
首先獲取到能處理InoutStream的Encoder也就是StreamEncoder,接着內部會調用到DiskLruCacheWrapper.put,方法內部又會執行Encoder.encode將輸入流寫入到文件中,這就不展開了。而且最後將sourceCacheGenerator進行了賦值(注意在建立DataCacheGenerator時傳入的callback是SourceGenerator自己),因此會執行DataCacheGenerator.startNext
// DataCacheGenerator.java
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
複製代碼
內部根據緩存Key獲取到剛剛保存的緩存文件,而後獲取全部能處理Model(File)類型的ModelLoader,接着將其遍歷構建LoadData,這裏就不看getModelLoaders內部實現邏輯,直接用ByteBufferFileLoader進行分析,經過調用buildLoadData會構建出一個ByteBufferFetcher實例,接着看看其loadData方法
// ByteBufferFetcher.java
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result);
}
複製代碼
將文件中全部的內容所有讀取到ByteBuffer中後回調onDataReady方法內部通過一層層調用最終調用了DecodeJob.decodeFromRetrievedData,到如今爲止數據還所有在這個byteBuffer中去,接下來就是尋找對應的decoder將其加載成一個Bitmap而後設置給ImageView,通過一系列判斷,最終會尋找到ByteBufferBitmapDecoder
public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height, @NonNull Options options) throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
複製代碼
downSampler裏面作了真正的操做,好比縮放圖片適應當前的ImageView大小等,接着判斷是否要將轉化後的圖片進行緩存,最後就通知外界圖片加載成功。
// DecodeJob.java
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
// EngineJob.java
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static final Handler MAIN_THREAD_HANDLER =
new Handler(Looper.getMainLooper(), new MainThreadCallback());
複製代碼
那麼最後接受消息的就是MainThreadCallback
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
...
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
複製代碼
這裏最終通過一層層回調會在主線程調用ImageViewTarget.onResourceReady
// ImageViewTarget.java
public void onResourceReady(@NonNull 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) {
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
複製代碼
到此爲止一張圖片就加載完畢了