承接上一篇文章
輔助文章
其他系列文章:
【一篇就懂系列】Glide源碼分析之緩存處理
Glide圖片加載庫從v3遷移到v4的改變和使用
3.into
最復雜的過程也是都在這里了拨黔,這個方法返回了一個Target對象钱磅,Target對象則是用來最終展示圖片用的(Target可以說是這一整篇文章的終極boss了杖爽,我們獲取的圖片就由它來操作)警没。那我們現(xiàn)在就來一步步探究圖片是怎么獲取的:
RequestBuilder類
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
glideContext.buildImageViewTarget(view, transcodeClass)通過這個方法構建出Target對象
GlideContext類
public <X> Target<X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
ImageViewTargetFactory類
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
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)");
}
}
}
這里知道展示圖片的類為BitmapImageViewTarge或DrawableImageViewTarget,如果當時有asBitmap()操作,那就由BitmapImageViewTarge操作圖片鲜漩,有asGif()操作或是默認的asDrawable()操作徘禁,就由DrawableImageViewTarget操作。本文章就以DrawableImageViewTarget示例千贯。短時期呢不會提到這個類屯仗,等你在文章中再看到的時候,就說明我們要成功了~~文章要結束了
這里有提到的 RequestOptions 搔谴,也就是請求選項魁袜,對于這類方法:
centerCrop()
placeholder()
error()
priority()
diskCacheStrategy()
都封裝在RequestOptions這個類中
into(imageview)的方法return又去調用into的另一個重載,
into( glideContext.buildImageViewTarget(view, transcodeClass),null,requestOptions);
這個方法源碼如下
RequestBuilder類
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and untracking Targets, and obtaining View dimensions that
// are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
接下來我們關注的重點就是Request了
Request request = buildRequest(target, targetListener, options);
通過buildRuquest構建一個request敦第,構建的過程也是層層方法的獲取峰弹,就不把中間的方法放出來了,就把調用的最后一個能返回得到Request的那個方法放出:
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListener,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
中間的代碼也有很多總結起來就是將url芜果、監(jiān)聽器鞠呈、RequestOption的一系列參數(shù)傳給Request。去做最終的請求师幕。那么越過構建request的過程粟按,我們接下來繼續(xù)分析into重載的那個方法:
去判斷是否這個target(imageview)有request對象诬滩,如果有,并且和之前的request相等灭将,就把當前的request 釋放掉疼鸟,并判斷之前的request沒有在工作,就讓request 重新開始
而如果當前請求不同于之前庙曙,就在RequestManager中清除之前的請求空镜,并setRequest(request),然后調用 requestManager.track(target, request);這個方法就是request開始的地方! 也就是runRequest()
方法捌朴;
RequestManager類
public class RequestManager implements LifecycleListener {
void track(Target<?> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
}
RequestTracker 類
public class RequestTracker {
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
}
現(xiàn)在就看Request 的begin方法了吴攒。看上面我們獲得的Request對象是SingleRequest砂蔽,所以去找SingleRequest類的begin方法
SingleRequest類
@Override
public void begin() {
assertNotCallingCallbacks();
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;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
在這里我們可以找到許多我們使用API的底層原理了洼怔,
比如典型的placeholder()和error(),
當請求完成之前左驾,會調用target.onLoadStarted(getPlaceholderDrawable());顯示占位圖镣隶,當model為null ,也就是我們load()中傳入的參數(shù)诡右,url安岂、file之類的為null時,我們跟著onLoadFailded走下去最終會看到target.onLoadFailed(getErrorDrawable());這樣的方法帆吻。
而真正的圖片加載分為兩種:
-
一種是我們在加載時使用了override(width,height) API為圖片指定了一個固定的寬高域那,此時會調用了 onSizeReady(overrideWidth, overrideHeight);
-
而我們不指定時,會調用target.getSize(this);猜煮。這個target.getSize()方法的內部會根據(jù)ImageView的layout_width和layout_height值做一系列的計算次员,來算出圖片應該的寬高。具體的計算細節(jié)我就不帶著大家分析了友瘤,總之在計算完之后翠肘,它也會調用onSizeReady()方法。
也就是說辫秧,不管是哪種情況,最終都會調用到onSizeReady()方法被丧,那我們就繼續(xù)看這個方法了盟戏。
SingleRequest類
@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.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
然后我們明顯可以看到這個方法的重點為load,類Engine的load方法甥桂。
那我們就繼續(xù)找到Engine類柿究,看它的load方法
Engine類
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
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;
}
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, useAnimationPool);
DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
loadFromCache(key, isMemoryCacheable)和loadFromActiveResources(key, isMemoryCacheable);緩存處理,檢查圖片是否在內存中黄选,如果是則直接返回圖片蝇摸,然后EngineJob<?> current = jobs.get(key);也是判斷當前任務是否在處理的隊列中婶肩。
所以對于沒有找到圖片的情況下,處理加載圖片的代碼是接下來的那部分貌夕,engineJobFactory.build律歼,構建了一個EngineJob,EngineJob充當了管理和調度者啡专,主要負責加載和各類回調通知险毁,開啟線程。構建一個DecodeJob们童,真正干活的勞動者畔况,這個類實現(xiàn)了Runnable接口,然后作為標識存在jobs這個map中慧库,增加回調跷跪,然后開啟線程。
具體看一下構建DecodeJob做了哪些重要操作
DecodeJob類
DecodeJob<R> init(
GlideContext glideContext,
Object model,
EngineKey loadKey,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
boolean onlyRetrieveFromCache,
Options options,
Callback<R> callback,
int order) {
decodeHelper.init(
glideContext,
model,
signature,
width,
height,
diskCacheStrategy,
resourceClass,
transcodeClass,
priority,
options,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
diskCacheProvider);
this.glideContext = glideContext;
this.signature = signature;
this.priority = priority;
this.loadKey = loadKey;
this.width = width;
this.height = height;
this.diskCacheStrategy = diskCacheStrategy;
this.onlyRetrieveFromCache = onlyRetrieveFromCache;
this.options = options;
this.callback = callback;
this.order = order;
this.runReason = RunReason.INITIALIZE;
return this;
}
這里我們看到了decodeHelper齐板,找到DecodeHelper類吵瞻,繼續(xù)看
DecodeHelper類
<R> DecodeHelper<R> init(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
DiskCacheStrategy diskCacheStrategy,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
Options options,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
DecodeJob.DiskCacheProvider diskCacheProvider) {
this.glideContext = glideContext;
this.model = model;
this.signature = signature;
this.width = width;
this.height = height;
this.diskCacheStrategy = diskCacheStrategy;
this.resourceClass = resourceClass;
this.diskCacheProvider = diskCacheProvider;
this.transcodeClass = (Class<Transcode>) transcodeClass;
this.priority = priority;
this.options = options;
this.transformations = transformations;
this.isTransformationRequired = isTransformationRequired;
this.isScaleOnlyOrNoTransform = isScaleOnlyOrNoTransform;
return (DecodeHelper<R>) this;
}
之所以要看這里,是這里傳遞了一些重要的類覆积。比如DecodeHelper听皿、DiskCacheProvider。我們先要在此有這個印象宽档,之后再做分析尉姨。
接著返回來繼續(xù)看load的看EngineJob的start方法
EngineJob類
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
有關緩存的決定下一篇文章單拿出來研究,這里先不看緩存又厉, 所以GlideExecutor選擇不從磁盤讀取緩存覆致,即getActiveSourceExecutor()肺蔚, executor.execute(decodeJob);這個方法宣羊,找到GlideExecutor的 execute
GlideExecutor類
@Override
public void execute(Runnable command) {
if (executeSynchronously) {
command.run();
} else {
super.execute(command);
}
}
現(xiàn)在我們要看DecodeJob的run方法了
DecodeJob類
@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");
// Methods in the try statement can invalidate currentFetcher, so set a local variable here to
// ensure that the fetcher is cleaned up either way.
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, t);
}
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
} finally {
// Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
// close in all cases anyway.
if (localFetcher != null) {
localFetcher.cleanup();
}
TraceCompat.endSection();
}
}
然后看這個方法
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
在DecodeJob的init的方法中this.runReason = RunReason.INITIALIZE;
所以我們先看getNextStage
DecodeJob類
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
主要是返回Stage值仇冯,這個Stage的作用就是標識當前為哪個階段之宿,對應不同的階段就要做不同的事。在還沒有緩存情況苛坚,我們首先應該做的是加載比被。所以遞歸調用這個函數(shù)色难,最終返回的Stage.SOURCE
然后看getNextGenerator()和 runGenerators()以及SourceGenerator
DecodeJob類
// 根據(jù)Stage找到數(shù)據(jù)抓取生成器。
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
// 產(chǎn)生含有降低采樣/轉換資源數(shù)據(jù)緩存文件的DataFetcher等缀。
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// 產(chǎn)生包含原始未修改的源數(shù)據(jù)緩存文件的DataFetcher枷莉。
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// 生成使用注冊的ModelLoader和加載時提供的Model獲取源數(shù)據(jù)規(guī)定的DataFetcher。
// 根據(jù)不同的磁盤緩存策略项滑,源數(shù)據(jù)可首先被寫入到磁盤依沮,然后從緩存文件中加載,而不是直接返回枪狂。
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.
}
顯而易見危喉,getNextGenerator()返回new SourceGenerator(decodeHelper, this);生成使用注冊的ModelLoader和加載時提供的Model獲取源數(shù)據(jù)規(guī)定的DataFetcher。
while判斷中isStarted = currentGenerator.startNext()也就是SourceGenerator.startNext()州疾,我們看這個方法首先started=true,runGenerators()退出循環(huán)辜限。
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;
}
然后根據(jù)model的fetcher加載數(shù)據(jù): loadData.fetcher.loadData(helper.getPriority(), this);
就是在這里薄嫡,我們找到了發(fā)起實際網(wǎng)絡請求的地方,而真正的操作是由HttpUrlFetcher類完成
HttpUrlFetcher類
@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();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (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);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == -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)絡請求了。
加載出來的數(shù)據(jù)即為stream
數(shù)據(jù)返回成功后調用了 callback.onDataReady(result);把數(shù)據(jù)回調返回,這個callback的實現(xiàn)是SourceGenerator
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);
}
}
這onDateReady方法中腥沽,在這篇文章中我們主要是忽略了緩存,所以執(zhí)行
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
cb為DecodeJob
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();
}
}
}
這里我們看到先做了線程的判斷,這樣做的目的是切換線程起便,我們要保證當前的線程為Glide自定義的線程中妙痹,因為下載都是借用第三方網(wǎng)絡庫琳轿,而此時有可能因為下載在別人的線程,所以要做一個判斷,假如不是Glide的線程颠毙,那么 runReason = RunReason.DECODE_DATA; callback.reschedule(this);也就是EngineJob的reschedule
EngineJob類
@Override
public void reschedule(DecodeJob<?> job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
getActiveSourceExecutor().execute(job);
}
這樣我們又重新回到GlideExecutor 的execute方法增蹭,同樣壮池,也就是回到DecodeJob.runWrapped()方法,而此時runReason 為DECODE_DATA橙依,執(zhí)行decodeFromRetrievedData();也就是判斷一下線程漆枚,但最終都是要回到DecodeJob.decodeFromRetrievedData()方法软族。
DecodeJob類
//1
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
//2
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
//3
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
//4
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
LoadPath類:
//5
public Resource<Transcode> load(DataRewinder<Data> rewinder, Options options, int width,
int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = listPool.acquire();
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
//6.
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder, Options options,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions) throws GlideException {
int size = decodePaths.size();
Resource<Transcode> result = null;
for (int i = 0; i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
DecodePath類
//7
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
接著看
DecodePath類
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
int height, Options options) throws GlideException {
List<Throwable> exceptions = listPool.acquire();
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
現(xiàn)在繼續(xù)執(zhí)行, result = decoder.decode(data, width, height, options);
找到對應的Decoder--此時的decoder是一個BitmapDrawableDecoder對象
public class BitmapDrawableDecoder<DataType> implements ResourceDecoder<DataType, BitmapDrawable> {
private final ResourceDecoder<DataType, Bitmap> decoder;
private final Resources resources;
private final BitmapPool bitmapPool;
public BitmapDrawableDecoder(Context context, ResourceDecoder<DataType, Bitmap> decoder) {
this(context.getResources(), Glide.get(context).getBitmapPool(), decoder);
}
public BitmapDrawableDecoder(Resources resources, BitmapPool bitmapPool,
ResourceDecoder<DataType, Bitmap> decoder) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapPool = Preconditions.checkNotNull(bitmapPool);
this.decoder = Preconditions.checkNotNull(decoder);
}
@Override
public boolean handles(DataType source, Options options) throws IOException {
return decoder.handles(source, options);
}
@Override
public Resource<BitmapDrawable> decode(DataType source, int width, int height, Options options)
throws IOException {
Resource<Bitmap> bitmapResource = decoder.decode(source, width, height, options);
if (bitmapResource == null) {
return null;
}
return LazyBitmapDrawableResource.obtain(resources, bitmapPool, bitmapResource.get());
}
}
Resource<Bitmap> bitmapResource = decoder.decode(source, width, height, options);
然后繼續(xù)找真正執(zhí)行decode方法的的Decoder温峭。
最終的decoder是一個StreamBitmapDecoder對象奸忽,找到這個類,看它的decode方法
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
private final Downsampler downsampler;
private final ArrayPool byteArrayPool;
public StreamBitmapDecoder(Downsampler downsampler, ArrayPool byteArrayPool) {
this.downsampler = downsampler;
this.byteArrayPool = byteArrayPool;
}
@Override
public boolean handles(InputStream source, Options options) throws IOException {
return downsampler.handles(source);
}
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height, Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a
// way to determine if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still
// attempt to decode the full image even when the header decode fails and/or overflows our read
// buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
}
可以看大最終它返回的是downsampler.decode禁炒,那我們再繼續(xù)找到Downsampler的decode
有點想哭暴备,終于找到對圖片的處理了涯捻。
Downsampler類
//1
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
+ " mark()");
byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
bitmapFactoryOptions.inTempStorage = bytesForOptions;
DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
DownsampleStrategy downsampleStrategy = options.get(DOWNSAMPLE_STRATEGY);
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
boolean isHardwareConfigAllowed =
options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);
if (decodeFormat == DecodeFormat.PREFER_ARGB_8888_DISALLOW_HARDWARE) {
isHardwareConfigAllowed = false;
}
try {
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions, byte[].class);
}
}
//2
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
long startTime = LogTime.getLogTime();
int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
int sourceWidth = sourceDimensions[0];
int sourceHeight = sourceDimensions[1];
String sourceMimeType = options.outMimeType;
// If we failed to obtain the image dimensions, we may end up with an incorrectly sized Bitmap,
// so we want to use a mutable Bitmap type. One way this can happen is if the image header is so
// large (10mb+) that our attempt to use inJustDecodeBounds fails and we're forced to decode the
// full size image.
if (sourceWidth == -1 || sourceHeight == -1) {
isHardwareConfigAllowed = false;
}
int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);
int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;
ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);
calculateScaling(
imageType,
is,
callbacks,
bitmapPool,
downsampleStrategy,
degreesToRotate,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
options);
calculateConfig(
is,
decodeFormat,
isHardwareConfigAllowed,
isExifOrientationRequired,
options,
targetWidth,
targetHeight);
boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
int expectedWidth;
int expectedHeight;
if (sourceWidth >= 0 && sourceHeight >= 0
&& fixBitmapToRequestedDimensions && isKitKatOrGreater) {
expectedWidth = targetWidth;
expectedHeight = targetHeight;
} else {
float densityMultiplier = isScaling(options)
? (float) options.inTargetDensity / options.inDensity : 1f;
int sampleSize = options.inSampleSize;
int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
expectedWidth = Math.round(downsampledWidth * densityMultiplier);
expectedHeight = Math.round(downsampledHeight * densityMultiplier);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Calculated target [" + expectedWidth + "x" + expectedHeight + "] for source"
+ " [" + sourceWidth + "x" + sourceHeight + "]"
+ ", sampleSize: " + sampleSize
+ ", targetDensity: " + options.inTargetDensity
+ ", density: " + options.inDensity
+ ", density multiplier: " + densityMultiplier);
}
}
// If this isn't an image, or BitmapFactory was unable to parse the size, width and height
// will be -1 here.
if (expectedWidth > 0 && expectedHeight > 0) {
setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
}
}
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logDecode(sourceWidth, sourceHeight, sourceMimeType, options, downsampled,
requestedWidth, requestedHeight, startTime);
}
Bitmap rotated = null;
if (downsampled != null) {
// If we scaled, the Bitmap density will be our inTargetDensity. Here we correct it back to
// the expected density dpi.
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
現(xiàn)在已經(jīng)得到這個最終的bitmap了晾捏,返回的Bitmap。
但StreamBitmapDecoder的decode方法返回的Resource<Bitmap> 仓手,所以通過BitmapResource.obtain(result, bitmapPool),將Bitmap對象包裝成了Resource<Bitmap>對象。代碼如下:
public class BitmapResource implements Resource<Bitmap>,
Initializable {
private final Bitmap bitmap;
private final BitmapPool bitmapPool;
/**
* Returns a new {@link BitmapResource} wrapping the given {@link Bitmap} if the Bitmap is
* non-null or null if the given Bitmap is null.
*
* @param bitmap A Bitmap.
* @param bitmapPool A non-null {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool}.
*/
@Nullable
public static BitmapResource obtain(@Nullable Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
return null;
} else {
return new BitmapResource(bitmap, bitmapPool);
}
}
public BitmapResource(Bitmap bitmap, BitmapPool bitmapPool) {
this.bitmap = Preconditions.checkNotNull(bitmap, "Bitmap must not be null");
this.bitmapPool = Preconditions.checkNotNull(bitmapPool, "BitmapPool must not be null");
}
@Override
public Class<Bitmap> getResourceClass() {
return Bitmap.class;
}
@Override
public Bitmap get() {
return bitmap;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
bitmapPool.put(bitmap);
}
@Override
public void initialize() {
bitmap.prepareToDraw();
}
}
經(jīng)過這樣一層包裝之后馏臭,如果我還需要獲取Bitmap帮寻,只需要調用Resource<Bitmap>的get()方法就可以了。
然后我們需要一層層繼續(xù)向上返回,Downsampler將decode返回值返回到StreamBitmapDecoder,StreamBitmapDecoder將返回值返回到BitmapDrawableDecoder舅列,然后BitmapDrawableDecoder的decode方法返回需要返回一個Resource<BitmapDrawable>對象榨惠,因此使用LazyBitmapDrawableResource.obtain(resources, bitmapPool, bitmapResource.get());進行封裝,如下封裝成了Resource<BitmapDrawable>對象简烤。
public class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
Initializable {
private final Bitmap bitmap;
private final Resources resources;
private final BitmapPool bitmapPool;
public static LazyBitmapDrawableResource obtain(Context context, Bitmap bitmap) {
return obtain(context.getResources(), Glide.get(context).getBitmapPool(), bitmap);
}
public static LazyBitmapDrawableResource obtain(Resources resources, BitmapPool bitmapPool,
Bitmap bitmap) {
return new LazyBitmapDrawableResource(resources, bitmapPool, bitmap);
}
LazyBitmapDrawableResource(Resources resources, BitmapPool bitmapPool, Bitmap bitmap) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapPool = Preconditions.checkNotNull(bitmapPool);
this.bitmap = Preconditions.checkNotNull(bitmap);
}
@Override
public Class<BitmapDrawable> getResourceClass() {
return BitmapDrawable.class;
}
@Override
public BitmapDrawable get() {
return new BitmapDrawable(resources, bitmap);
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
bitmapPool.put(bitmap);
}
@Override
public void initialize() {
bitmap.prepareToDraw();
}
}
繼續(xù)向上找榨馁,BitmapDrawableDecoder的decode的值返回賦給DecodePath的decode方法的decoded值死陆,繼續(xù)看DecodePath的decode方法做了什么
DecodePath類
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
首先callback.onResourceDecoded(decoded)规哪,把數(shù)據(jù)回調返回給上層的DecodeJob
private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {
private final DataSource dataSource;
@Synthetic
DecodeCallback(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Resource<Z> onResourceDecoded(Resource<Z> decoded) {
Class<Z> resourceSubClass = getResourceClass(decoded);
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
if (encodeStrategy == EncodeStrategy.SOURCE) {
key = new DataCacheKey(currentSourceKey, signature);
} else if (encodeStrategy == EncodeStrategy.TRANSFORMED) {
key = new ResourceCacheKey(currentSourceKey, signature, width, height,
appliedTransformation, resourceSubClass, options);
} else {
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
@SuppressWarnings("unchecked")
private Class<Z> getResourceClass(Resource<Z> resource) {
return (Class<Z>) resource.get().getClass();
}
}
然后decode返回了一個Resource<Transcode>類型 蚊逢,其實這里的Transcode就是BitmapDrawable類型
這個transcoder類,是一個BitmapDrawableTranscoder 對象
這里調用了一個transcode方法戳表,
public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
private final Resources resources;
private final BitmapPool bitmapPool;
public BitmapDrawableTranscoder(Context context) {
this(context.getResources(), Glide.get(context).getBitmapPool());
}
public BitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapPool = Preconditions.checkNotNull(bitmapPool);
}
@Override
public Resource<BitmapDrawable> transcode(Resource<Bitmap> toTranscode, Options options) {
return LazyBitmapDrawableResource.obtain(resources, bitmapPool, toTranscode.get());
//返回的也是就是一個Resource<BitmapDrawable>類型
}
}
接著向上返回,DecodePath將decode的值返回給LoadPath色瘩,LoadPath再返回給DecodeJob席覆,前面翻過去太多了晦毙,我再粘一遍這個類里的主要幾個方法
DecodeJob類
//1
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
//2
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
//3
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
//4
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
從這里可以看到從DecodeJob初始化LoadPath類時傳的是參數(shù)R牲尺,所以Resource<R>和Resource<Transcode>是一樣的瘸羡,也就是Resource<BitmapDrawable>垮兑。此時我們已經(jīng)回到觸發(fā)裝載的
decodeFromRetrievedData()方法了~
此時resource!=null系枪,調用notifyEncodeAndRelease方法
//1
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();
}
}
//2
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
這里調用了onResourceReady雾棺,而此callback的實現(xiàn)是EngineJob對象完成工秩,所以找到EngineJob的onResourceReady螟碎。
EngineJob中
//1
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
//2
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;
}
}
//3
@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*/);
}
onResourceReady()方法使用Handler發(fā)出了一條MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就會收到這條消息迹栓。從這里開始掉分,所有的邏輯又回到主線程當中進行了,因為很快就需要更新UI了克伊。
在handleResultOnMainThread()方法中酥郭,我們看到通過一個循環(huán),調用了所有ResourceCallback的onResourceReady()方法不从。那么這個ResourceCallback是什么呢惫叛?答案在addCallback()方法當中帆喇,它會向cbs集合中去添加ResourceCallback
EngineJob
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
stateVerifier.throwIfRecycled();
if (hasResource) {
cb.onResourceReady(engineResource, dataSource);
} else if (hasLoadFailed) {
cb.onLoadFailed(exception);
} else {
cbs.add(cb);
}
}
而addCallback()的調用,是在我們之前Engine.load(xx爬橡,xx,xx鹿榜,xx,xx埠啃,.....,RescourceCallback cb)方法中傳入的,engineJob.addCallback(cb);可以自己向上翻翻翻良价。。那Engine的又是誰傳過來的呢俱尼?是在SingleRequest類的onSizeReady中,它傳入了this裸违,所以EngineJob的 cb.onResourceReady(engineResource, dataSource);最終回調是在SingleRequest中
SingleRequest
//1
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);
}
//2
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");
}
isCallingCallbacks = true;
try {
if ((requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
&& (targetListener == null
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
回調的是第一個onResourceReady方法卤唉,我們可以看到 Object received = resource.get(); 獲得了我們剛才封裝的圖片對象挡鞍,就是我們剛才說薄翅,直接通過get方法即可獲得。我們這里指的是BitmapDrawable對象倒信。
然后將這個值傳入到了第二個onResourceReady()方法當中,并調用了target.onResourceReady(result, animation);
那么這個target又是什么呢?翻到文章初始化竟块,在into()方法的一開始辕翰,我們就分析了在into()方法的最后一行务甥,調用了glide.buildImageViewTarget()方法來構建出一個Target怒详,而這個Target就是一個DrawableImageViewTarget對象。當時我們就說這個Target就是我們這篇文章的終極boss缎岗【材幔看到它就代表結束了。
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
看到 view.setImageDrawable(resource);已經(jīng)開心到不能自己,那你可能會問鼠渺,onResourceReady(result, animation);在哪呢鸭巴,肯定是父類啊,看一眼
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> {
...
@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) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected abstract void setResource(@Nullable Z resource);
}