【两篇就懂系列】Glide源码分析之加载图片流程(2/2)
2017-12-24 本文已影响893人
芒果味的你呀
承接上一篇文章
辅助文章
其他系列文章:
【一篇就懂系列】Glide源码分析之缓存处理
Glide图片加载库从v3迁移到v4的改变和使用
3.into
最复杂的过程也是都在这里了,这个方法返回了一个Target对象,Target对象则是用来最终展示图片用的(Target可以说是这一整篇文章的终极boss了,我们获取的图片就由它来操作)。那我们现在就来一步步探究图片是怎么获取的:
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、监听器、RequestOption的一系列参数传给Request。去做最终的请求。那么越过构建request的过程,我们接下来继续分析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);
}
}
}
现在就看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()中传入的参数,url、file之类的为null时,我们跟着onLoadFailded走下去最终会看到target.onLoadFailed(getErrorDrawable());这样的方法。
而真正的图片加载分为两种:
-
一种是我们在加载时使用了override(width,height) API为图片指定了一个固定的宽高,此时会调用了 onSizeReady(overrideWidth, overrideHeight);
-
而我们不指定时,会调用target.getSize(this);。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用onSizeReady()方法。
也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,那我们就继续看这个方法了。
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方法。
那我们就继续找到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,真正干活的劳动者,这个类实现了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类,继续看
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。我们先要在此有这个印象,之后再做分析。
接着返回来继续看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);
}
}
现在我们要看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的作用就是标识当前为哪个阶段,对应不同的阶段就要做不同的事。在还没有缓存情况,我们首先应该做的是加载。所以递归调用这个函数,最终返回的Stage.SOURCE
然后看getNextGenerator()和 runGenerators()以及SourceGenerator
DecodeJob类
// 根据Stage找到数据抓取生成器。
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
// 产生含有降低采样/转换资源数据缓存文件的DataFetcher。
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// 产生包含原始未修改的源数据缓存文件的DataFetcher。
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// 生成使用注册的ModelLoader和加载时提供的Model获取源数据规定的DataFetcher。
// 根据不同的磁盘缓存策略,源数据可首先被写入到磁盘,然后从缓存文件中加载,而不是直接返回。
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获取源数据规定的DataFetcher。
while判断中isStarted = currentGenerator.startNext()也就是SourceGenerator.startNext(),我们看这个方法首先started=true,runGenerators()退出循环。
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;
}
然后根据model的fetcher加载数据: loadData.fetcher.loadData(helper.getPriority(), this);
就是在这里,我们找到了发起实际网络请求的地方,而真正的操作是由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;
}
终于找到网络请求了。
加载出来的数据即为stream
数据返回成功后调用了 callback.onDataReady(result);把数据回调返回,这个callback的实现是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方法中,在这篇文章中我们主要是忽略了缓存,所以执行
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自定义的线程中,因为下载都是借用第三方网络库,而此时有可能因为下载在别人的线程,所以要做一个判断,假如不是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,执行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;
}
现在继续执行, 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);
然后继续找真正执行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,那我们再继续找到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;
}
现在已经得到这个最终的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();
}
}
经过这样一层包装之后,如果我还需要获取Bitmap,只需要调用Resource<Bitmap>的get()方法就可以了。
然后我们需要一层层继续向上返回,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();
}
}
继续向上找,BitmapDrawableDecoder的decode的值返回赋给DecodePath的decode方法的decoded值,继续看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),把数据回调返回给上层的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类时传的是参数R,所以Resource<R>和Resource<Transcode>是一样的,也就是Resource<BitmapDrawable>。此时我们已经回到触发装载的
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的实现是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发出了一条MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就会收到这条消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新UI了。
在handleResultOnMainThread()方法中,我们看到通过一个循环,调用了所有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);已经开心到不能自己,那你可能会问,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);
}