3. into()
如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为into()方法也是整个Glide图片加载流程中逻辑最复杂的地方。
不过从刚才的代码来看,into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。
DrawableRequestBuilder的父类是GenericRequestBuilder,我们来看一下GenericRequestBuilder类中的into()方法,如下所示:
public Target into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
这里前面一大堆的判断逻辑我们都可以先不用管,等到后面文章讲transform的时候会再进行解释,现在我们只需要关注最后一行代码。最后一行代码先是调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码:
Target buildImageViewTarget(ImageView imageView, Class transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
这里其实又是调用了ImageViewTargetFactory的buildTarget()方法,我们继续跟进去,代码如下所示:
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public Target buildTarget(ImageView view, Class clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
可以看到,在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。那如果你要分析这个class参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳理清楚。这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。
也就是说,通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。我们来看一下这个into()方法的源码:
public > Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
这里我们还是只抓核心代码,其实只有两行是最关键的,第15行调用buildRequest()方法构建出了一个Request对象,还有第18行来执行这个Request。
Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
private Request buildRequest(Target target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
private Request obtainRequest(Target target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
可以看到,buildRequest()方法的内部其实又调用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代码虽然有点长,但是其中90%的代码都是在处理缩略图的。如果我们只追主线流程的话,那么只需要看第47行代码就可以了。这里调用了obtainRequest()方法来获取一个Request对象,而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。那么我们进入到这个GenericRequest的obtain()方法瞧一瞧:
public final class GenericRequest implements Request, SizeReadyCallback,
ResourceCallback {
...
public static GenericRequest obtain(
LoadProvider loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target target,
float sizeMultiplier,
Drawable placeholderDrawable,
int placeholderResourceId,
Drawable errorDrawable,
int errorResourceId,
Drawable fallbackDrawable,
int fallbackResourceId,
RequestListener super A, R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
Transformation transformation,
Class transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest request = (GenericRequest) REQUEST_POOL.poll();
if (request == null) {
request = new GenericRequest();
}
request.init(loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderResourceId,
errorDrawable,
errorResourceId,
fallbackDrawable,
fallbackResourceId,
requestListener,
requestCoordinator,
engine,
transformation,
transcodeClass,
isMemoryCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
return request;
}
...
}
可以看到,这里在第33行去new了一个GenericRequest对象,并在最后一行返回,也就是说,obtain()方法实际上获得的就是一个GenericRequest对象。另外这里又在第35行调用了GenericRequest的init(),里面主要就是一些赋值的代码,将传入的这些参数赋值到GenericRequest的成员变量当中,我们就不再跟进去看了。
好,那现在解决了构建Request对象的问题,接下来我们看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示:
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
这里有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
暂停请求的功能仍然不是这篇文章所关心的,这里就直接忽略了,我们重点来看这个begin()方法。由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
这里我们来注意几个细节,首先如果model等于null,model也就是我们在第二步load()方法中传入的图片URL地址,这个时候会调用onException()方法。如果你跟到onException()方法里面去看看,你会发现它最终会调用到一个setErrorPlaceholder()当中,如下所示:
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
这个方法中会先去获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用target.onLoadFailed()方法并将占位图传入。那么onLoadFailed()方法中做了什么呢?我们看一下:
public abstract class ImageViewTarget extends ViewTarget implements GlideAnimation.ViewAdapter {
...
@Override
public void onLoadStarted(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setImageDrawable(errorDrawable);
}
...
}
很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的placeholder()和error()这两个占位图API底层的实现原理。
好,那么我们继续回到begin()方法。刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。如果没指定的话,就会执行第12行代码,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用onSizeReady()方法。也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,在这里进行下一步操作。那么我们跟到这个方法里面来:
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader modelLoader = loadProvider.getModelLoader();
final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
从这里开始,真正复杂的地方来了,我们需要慢慢进行分析。先来看一下,在第12行调用了loadProvider.getModelLoader()方法,那么我们第一个要搞清楚的就是,这个loadProvider是什么?要搞清楚这点,需要先回到第二步的load()方法当中。还记得load()方法是返回一个DrawableTypeRequest对象吗?刚才我们只是分析了DrawableTypeRequest当中的asBitmap()和asGif()方法,并没有仔细看它的构造函数,现在我们重新来看一下DrawableTypeRequest类的构造函数:
public class DrawableTypeRequest extends DrawableRequestBuilder implements DownloadOptions {
private final ModelLoader streamModelLoader;
private final ModelLoader fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static FixedLoadProvider buildProvider(Glide glide,
ModelLoader streamModelLoader,
ModelLoader fileDescriptorModelLoader, Class resourceClass,
Class transcodedClass,
ResourceTranscoder transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader modelLoader = new ImageVideoModelLoader(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class modelClass, ModelLoader streamModelLoader,
ModelLoader fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
...
}
可以看到,这里在第29行,也就是构造函数中,调用了一个buildProvider()方法,并把streamModelLoader和fileDescriptorModelLoader等参数传入到这个方法中,这两个ModelLoader就是之前在loadGeneric()方法中构建出来的。
那么我们再来看一下buildProvider()方法里面做了什么,在第16行调用了glide.buildTranscoder()方法来构建一个ResourceTranscoder,它是用于对图片进行转码的,由于ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象。
接下来在第18行调用了glide.buildDataProvider()方法来构建一个DataLoadProvider,它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。
然后在第20行,new了一个ImageVideoModelLoader的实例,并把之前loadGeneric()方法中构建的两个ModelLoader封装到了ImageVideoModelLoader当中。
最后,在第22行,new出一个FixedLoadProvider,并把刚才构建的出来的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。
好的,那么我们回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行,分别调用了loadProvider的getModelLoader()方法和getTranscoder()方法,那么得到的对象也就是刚才我们分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。而在第13行,又调用了ImageVideoModelLoader的getResourceFetcher()方法,这里我们又需要跟进去瞧一瞧了,代码如下所示:
public class ImageVideoModelLoader implements ModelLoader {
private static final String TAG = "IVML";
private final ModelLoader streamLoader;
private final ModelLoader fileDescriptorLoader;
public ImageVideoModelLoader(ModelLoader streamLoader,
ModelLoader fileDescriptorLoader) {
if (streamLoader == null && fileDescriptorLoader == null) {
throw new NullPointerException("At least one of streamLoader and fileDescriptorLoader must be non null");
}
this.streamLoader = streamLoader;
this.fileDescriptorLoader = fileDescriptorLoader;
}
@Override
public DataFetcher getResourceFetcher(A model, int width, int height) {
DataFetcher streamFetcher = null;
if (streamLoader != null) {
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
static class ImageVideoFetcher implements DataFetcher {
private final DataFetcher streamFetcher;
private final DataFetcher fileDescriptorFetcher;
public ImageVideoFetcher(DataFetcher streamFetcher,
DataFetcher fileDescriptorFetcher) {
this.streamFetcher = streamFetcher;
this.fileDescriptorFetcher = fileDescriptorFetcher;
}
...
}
}
可以看到,在第20行会先调用streamLoader.getResourceFetcher()方法获取一个DataFetcher,而这个streamLoader其实就是我们在loadGeneric()方法中构建出的StreamStringLoader,调用它的getResourceFetcher()方法会得到一个HttpUrlFetcher对象。然后在第28行new出了一个ImageVideoFetcher对象,并把获得的HttpUrlFetcher对象传进去。也就是说,ImageVideoModelLoader的getResourceFetcher()方法得到的是一个ImageVideoFetcher。
那么我们再次回到onSizeReady()方法,在onSizeReady()方法的第23行,这里将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来我们就要看一看,这个Engine的load()方法当中,到底做了什么?代码如下所示:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
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);
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 engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob decodeJob = new DecodeJob(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
load()方法中的代码虽然有点长,但大多数的代码都是在处理缓存的。关于缓存的内容我们会在下一篇文章当中学习,现在只需要从第45行看起就行。这里构建了一个EngineJob,它的主要作用就是用来开启线程的,为后面的异步加载图片做准备。接下来第46行创建了一个DecodeJob对象,从名字上来看,它好像是用来对图片进行解码的,但实际上它的任务十分繁重,待会我们就知道了。继续往下看,第48行创建了一个EngineRunnable对象,并且在51行调用了EngineJob的start()方法来运行EngineRunnable对象,这实际上就是让EngineRunnable的run()方法在子线程当中执行了。那么我们现在就可以去看看EngineRunnable的run()方法里做了些什么,如下所示:
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
这个方法中的代码并不多,但我们仍然还是要抓重点。在第9行,这里调用了一个decode()方法,并且这个方法返回了一个Resource对象。看上去所有的逻辑应该都在这个decode()方法执行的了,那我们跟进去瞧一瞧:
private Resource> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
decode()方法中又分了两种情况,从缓存当中去decode图片的话就会执行decodeFromCache(),否则的话就执行decodeFromSource()。本篇文章中我们不讨论缓存的情况,那么就直接来看decodeFromSource()方法的代码吧,如下所示:
private Resource> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
这里又调用了DecodeJob的decodeFromSource()方法。刚才已经说了,DecodeJob的任务十分繁重,我们继续跟进看一看吧:
class DecodeJob {
...
public Resource decodeFromSource() throws Exception {
Resource decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource decodeSource() throws Exception {
Resource decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
...
}
主要的方法就这些,我都帮大家提取出来了。那么我们先来看一下decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()方法来处理这个Resource对象。
那么我们先来看第一步,decodeSource()方法中的逻辑也并不复杂,首先在第14行调用了fetcher.loadData()方法。那么这个fetcher是什么呢?其实就是刚才在onSizeReady()方法中得到的ImageVideoFetcher对象,这里调用它的loadData()方法,代码如下所示:
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
InputStream is = null;
if (streamFetcher != null) {
try {
is = streamFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
return new ImageVideoWrapper(is, fileDescriptor);
}
可以看到,在ImageVideoFetcher的loadData()方法的第6行,这里又去调用了streamFetcher.loadData()方法,那么这个streamFetcher是什么呢?自然就是刚才在组装ImageVideoFetcher对象时传进来的HttpUrlFetcher了。因此这里又会去调用HttpUrlFetcher的loadData()方法,那么我们继续跟进去瞧一瞧:
public class HttpUrlFetcher implements DataFetcher {
...
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("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 IOException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
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;
}
...
}
经过一层一层地跋山涉水,我们终于在这里找到网络通讯的代码了!之前有朋友跟我讲过,说Glide的源码实在是太复杂了,甚至连网络请求是在哪里发出去的都找不到。我们也是经过一段一段又一段的代码跟踪,终于把网络请求的代码给找出来了,实在是太不容易了。
不过也别高兴得太早,现在离最终分析完还早着呢。可以看到,loadData()方法只是返回了一个InputStream,服务器返回的数据连读都还没开始读呢。所以我们还是要静下心来继续分析,回到刚才ImageVideoFetcher的loadData()方法中,在这个方法的最后一行,创建了一个ImageVideoWrapper对象,并把刚才得到的InputStream作为参数传了进去。
然后我们回到再上一层,也就是DecodeJob的decodeSource()方法当中,在得到了这个ImageVideoWrapper对象之后,紧接着又将这个对象传入到了decodeFromSourceData()当中,来去解码这个对象。decodeFromSourceData()方法的代码如下所示:
private Resource decodeFromSourceData(A data) throws IOException {
final Resource decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
可以看到,这里在第7行调用了loadProvider.getSourceDecoder().decode()方法来进行解码。loadProvider就是刚才在onSizeReady()方法中得到的FixedLoadProvider,而getSourceDecoder()得到的则是一个GifBitmapWrapperResourceDecoder对象,也就是要调用这个对象的decode()方法来对图片进行解码。那么我们来看下GifBitmapWrapperResourceDecoder的代码:
public class GifBitmapWrapperResourceDecoder implements ResourceDecoder {
...
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
result = decodeStream(source, width, height, bytes);
} else {
result = decodeBitmapWrapper(source, width, height);
}
return result;
}
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
throws IOException {
InputStream bis = streamFactory.build(source.getStream(), bytes);
bis.mark(MARK_LIMIT_BYTES);
ImageHeaderParser.ImageType type = parser.parse(bis);
bis.reset();
GifBitmapWrapper result = null;
if (type == ImageHeaderParser.ImageType.GIF) {
result = decodeGifWrapper(bis, width, height);
}
// Decoding the gif may fail even if the type matches.
if (result == null) {
// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
// pass in a new source containing the buffered stream rather than the original stream.
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
}
return result;
}
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
...
}
首先,在decode()方法中,又去调用了另外一个decode()方法的重载。然后在第23行调用了decodeStream()方法,准备从服务器返回的流当中读取数据。decodeStream()方法中会先从流中读取2个字节的数据,来判断这张图是GIF图还是普通的静图,如果是GIF图就调用decodeGifWrapper()方法来进行解码,如果是普通的静图就用调用decodeBitmapWrapper()方法来进行解码。这里我们只分析普通静图的实现流程,GIF图的实现有点过于复杂了,无法在本篇文章当中分析。
然后我们来看一下decodeBitmapWrapper()方法,这里在第52行调用了bitmapDecoder.decode()方法。这个bitmapDecoder是一个ImageVideoBitmapDecoder对象,那么我们来看一下它的代码,如下所示:
public class ImageVideoBitmapDecoder implements ResourceDecoder {
private final ResourceDecoder streamDecoder;
private final ResourceDecoder fileDescriptorDecoder;
public ImageVideoBitmapDecoder(ResourceDecoder streamDecoder,
ResourceDecoder fileDescriptorDecoder) {
this.streamDecoder = streamDecoder;
this.fileDescriptorDecoder = fileDescriptorDecoder;
}
@Override
public Resource decode(ImageVideoWrapper source, int width, int height) throws IOException {
Resource result = null;
InputStream is = source.getStream();
if (is != null) {
try {
result = streamDecoder.decode(is, width, height);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
}
}
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
...
}
代码并不复杂,在第14行先调用了source.getStream()来获取到服务器返回的InputStream,然后在第17行调用streamDecoder.decode()方法进行解码。streamDecode是一个StreamBitmapDecoder对象,那么我们再来看这个类的源码,如下所示:
public class StreamBitmapDecoder implements ResourceDecoder {
...
private final Downsampler downsampler;
private BitmapPool bitmapPool;
private DecodeFormat decodeFormat;
public StreamBitmapDecoder(Downsampler downsampler, BitmapPool bitmapPool, DecodeFormat decodeFormat) {
this.downsampler = downsampler;
this.bitmapPool = bitmapPool;
this.decodeFormat = decodeFormat;
}
@Override
public Resource decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
...
}
可以看到,它的decode()方法又去调用了Downsampler的decode()方法。接下来又到了激动人心的时刻了,Downsampler的代码如下所示:
public abstract class Downsampler implements BitmapDecoder {
...
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
// 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);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config config = getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
/**
* A method for getting the dimensions of an image from the given InputStream.
*
* @param is The InputStream representing the image.
* @param options The options to pass to
* {@link BitmapFactory#decodeStream(InputStream, android.graphics.Rect,
* BitmapFactory.Options)}.
* @return an array containing the dimensions of the image in the form {width, height}.
*/
public int[] getDimensions(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
options.inJustDecodeBounds = true;
decodeStream(is, bufferedStream, options);
options.inJustDecodeBounds = false;
return new int[] { options.outWidth, options.outHeight };
}
private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
// This is large, but jpeg headers are not size bounded so we need something large enough to minimize
// the possibility of not being able to fit enough of the header in the buffer to get the image size so
// that we don't fail to load images. The BufferedInputStream will create a new buffer of 2x the
// original size each time we use up the buffer space without passing the mark so this is a maximum
// bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
is.mark(MARK_POSITION);
} else {
// Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
// unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
// current buffer size here. See issue #225.
bufferedStream.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
try {
if (options.inJustDecodeBounds) {
is.reset();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
+ " sample=" + options.inSampleSize, e);
}
}
return result;
}
...
}
可以看到,对服务器返回的InputStream的读取,以及对图片的加载全都在这里了。当然这里其实处理了很多的逻辑,包括对图片的压缩,甚至还有旋转、圆角等逻辑处理,但是我们目前只需要关注主线逻辑就行了。decode()方法执行之后,会返回一个Bitmap对象,那么图片在这里其实也就已经被加载出来了,剩下的工作就是如果让这个Bitmap显示到界面上,我们继续往下分析。
回到刚才的StreamBitmapDecoder当中,你会发现,它的decode()方法返回的是一个Resource
public class BitmapResource implements Resource {
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 BitmapPool}.
*/
public static BitmapResource obtain(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
return null;
} else {
return new BitmapResource(bitmap, bitmapPool);
}
}
public BitmapResource(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
throw new NullPointerException("Bitmap must not be null");
}
if (bitmapPool == null) {
throw new NullPointerException("BitmapPool must not be null");
}
this.bitmap = bitmap;
this.bitmapPool = bitmapPool;
}
@Override
public Bitmap get() {
return bitmap;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
if (!bitmapPool.put(bitmap)) {
bitmap.recycle();
}
}
}
BitmapResource的源码也非常简单,经过这样一层包装之后,如果我还需要获取Bitmap,只需要调用Resource
然后我们需要一层层继续向上返回,StreamBitmapDecoder会将值返回到ImageVideoBitmapDecoder当中,而ImageVideoBitmapDecoder又会将值返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法当中。由于代码隔得有点太远了,我重新把decodeBitmapWrapper()方法的代码贴一下:
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
可以看到,decodeBitmapWrapper()方法返回的是一个GifBitmapWrapper对象。因此,这里在第5行,又将Resource
public class GifBitmapWrapper {
private final Resource gifResource;
private final Resource bitmapResource;
public GifBitmapWrapper(Resource bitmapResource, Resource gifResource) {
if (bitmapResource != null && gifResource != null) {
throw new IllegalArgumentException("Can only contain either a bitmap resource or a gif resource, not both");
}
if (bitmapResource == null && gifResource == null) {
throw new IllegalArgumentException("Must contain either a bitmap resource or a gif resource");
}
this.bitmapResource = bitmapResource;
this.gifResource = gifResource;
}
/**
* Returns the size of the wrapped resource.
*/
public int getSize() {
if (bitmapResource != null) {
return bitmapResource.getSize();
} else {
return gifResource.getSize();
}
}
/**
* Returns the wrapped {@link Bitmap} resource if it exists, or null.
*/
public Resource getBitmapResource() {
return bitmapResource;
}
/**
* Returns the wrapped {@link GifDrawable} resource if it exists, or null.
*/
public Resource getGifResource() {
return gifResource;
}
}
还是比较简单的,就是分别对gifResource和bitmapResource做了一层封装而已,相信没有什么解释的必要。
然后这个GifBitmapWrapper对象会一直向上返回,返回到GifBitmapWrapperResourceDecoder最外层的decode()方法的时候,会对它再做一次封装,如下所示:
@Override
public Resource decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
可以看到,这里在第11行,又将GifBitmapWrapper封装到了一个GifBitmapWrapperResource对象当中,最终返回的是一个Resource
public class GifBitmapWrapperResource implements Resource {
private final GifBitmapWrapper data;
public GifBitmapWrapperResource(GifBitmapWrapper data) {
if (data == null) {
throw new NullPointerException("Data must not be null");
}
this.data = data;
}
@Override
public GifBitmapWrapper get() {
return data;
}
@Override
public int getSize() {
return data.getSize();
}
@Override
public void recycle() {
Resource bitmapResource = data.getBitmapResource();
if (bitmapResource != null) {
bitmapResource.recycle();
}
Resource gifDataResource = data.getGifResource();
if (gifDataResource != null) {
gifDataResource.recycle();
}
}
}
经过这一层的封装之后,我们从网络上得到的图片就能够以Resource接口的形式返回,并且还能同时处理Bitmap图片和GIF图片这两种情况。
那么现在我们可以回到DecodeJob当中了,它的decodeFromSourceData()方法返回的是一个Resource
public Resource decodeFromSource() throws Exception {
Resource decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
刚才我们就是从这里跟进到decodeSource()方法当中,然后执行了一大堆一大堆的逻辑,最终得到了这个Resource
private Resource transformEncodeAndTranscode(Resource decoded) {
long startTime = LogTime.getLogTime();
Resource transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
private Resource transcode(Resource transformed) {
if (transformed == null) {
return null;
}
return transcoder.transcode(transformed);
}
首先,这个方法开头的几行transform还有cache,这都是我们后面才会学习的东西,现在不用管它们就可以了。需要注意的是第9行,这里调用了一个transcode()方法,就把Resource
而transcode()方法中又是调用了transcoder的transcode()方法,那么这个transcoder是什么呢?其实这也是Glide源码特别难懂的原因之一,就是它用到的很多对象都是很早很早之前就初始化的,在初始化的时候你可能完全就没有留意过它,因为一时半会根本就用不着,但是真正需要用到的时候你却早就记不起来这个对象是从哪儿来的了。
那么这里我来提醒一下大家吧,在第二步load()方法返回的那个DrawableTypeRequest对象,它的构建函数中去构建了一个FixedLoadProvider对象,然后我们将三个参数传入到了FixedLoadProvider当中,其中就有一个GifBitmapWrapperDrawableTranscoder对象。后来在onSizeReady()方法中获取到了这个参数,并传递到了Engine当中,然后又由Engine传递到了DecodeJob当中。因此,这里的transcoder其实就是这个GifBitmapWrapperDrawableTranscoder对象。那么我们来看一下它的源码:
public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder {
private final ResourceTranscoder bitmapDrawableResourceTranscoder;
public GifBitmapWrapperDrawableTranscoder(
ResourceTranscoder bitmapDrawableResourceTranscoder) {
this.bitmapDrawableResourceTranscoder = bitmapDrawableResourceTranscoder;
}
@Override
public Resource transcode(Resource toTranscode) {
GifBitmapWrapper gifBitmap = toTranscode.get();
Resource bitmapResource = gifBitmap.getBitmapResource();
final Resource extends GlideDrawable> result;
if (bitmapResource != null) {
result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
} else {
result = gifBitmap.getGifResource();
}
return (Resource) result;
}
...
}
这里我来简单解释一下,GifBitmapWrapperDrawableTranscoder的核心作用就是用来转码的。因为GifBitmapWrapper是无法直接显示到ImageView上面的,只有Bitmap或者Drawable才能显示到ImageView上。因此,这里的transcode()方法先从Resource
接下来做了一个判断,如果Resource
这里在第15行又进行了一次转码,是调用的GlideBitmapDrawableTranscoder对象的transcode()方法,代码如下所示:
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder {
private final Resources resources;
private final BitmapPool bitmapPool;
public GlideBitmapDrawableTranscoder(Context context) {
this(context.getResources(), Glide.get(context).getBitmapPool());
}
public GlideBitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) {
this.resources = resources;
this.bitmapPool = bitmapPool;
}
@Override
public Resource transcode(Resource toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
...
}
可以看到,这里new出了一个GlideBitmapDrawable对象,并把Bitmap封装到里面。然后对GlideBitmapDrawable再进行一次封装,返回一个Resource
现在再返回到GifBitmapWrapperDrawableTranscoder的transcode()方法中,你会发现它们的类型就一致了。因为不管是静图的Resource
那么我们继续回到DecodeJob当中,它的decodeFromSource()方法得到了Resource
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
也就是说,经过第9行decode()方法的执行,我们最终得到了这个Resource
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
这个manager就是EngineJob对象,因此这里实际上调用的是EngineJob的onResourceReady()方法,代码如下所示:
class EngineJob implements EngineRunnable.EngineRunnableManager {
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
private final List cbs = new ArrayList();
...
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
cb.onResourceReady(engineResource);
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
@Override
public void onResourceReady(final Resource> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
@Override
public void onException(final Exception e) {
this.exception = e;
MAIN_THREAD_HANDLER.obtainMessage(MSG_EXCEPTION, this).sendToTarget();
}
private void handleExceptionOnMainThread() {
if (isCancelled) {
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received an exception without any callbacks to notify");
}
hasException = true;
listener.onEngineJobComplete(key, null);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
cb.onException(exception);
}
}
}
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
...
}
可以看到,这里在onResourceReady()方法使用Handler发出了一条MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就会收到这条消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新UI了。
然后在第72行调用了handleResultOnMainThread()方法,这个方法中又通过一个循环,调用了所有ResourceCallback的onResourceReady()方法。那么这个ResourceCallback是什么呢?答案在addCallback()方法当中,它会向cbs集合中去添加ResourceCallback。那么这个addCallback()方法又是哪里调用的呢?其实调用的地方我们早就已经看过了,只不过之前没有注意,现在重新来看一下Engine的load()方法,如下所示:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder, Priority priority,
boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
...
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob decodeJob = new DecodeJob(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
这次把目光放在第18行上面,看到了吗?就是在这里调用的EngineJob的addCallback()方法来注册的一个ResourceCallback。那么接下来的问题就是,Engine.load()方法的ResourceCallback参数又是谁传过来的呢?这就需要回到GenericRequest的onSizeReady()方法当中了,我们看到ResourceCallback是load()方法的最后一个参数,那么在onSizeReady()方法中调用load()方法时传入的最后一个参数是什么?代码如下所示:
public final class GenericRequest implements Request, SizeReadyCallback,
ResourceCallback {
...
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader modelLoader = loadProvider.getModelLoader();
final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation,
transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
...
}
请将目光锁定在第29行的最后一个参数,this。没错,就是this。GenericRequest本身就实现了ResourceCallback的接口,因此EngineJob的回调最终其实就是回调到了GenericRequest的onResourceReady()方法当中了,代码如下所示:
public void onResourceReady(Resource> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("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.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
private void onResourceReady(Resource> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
这里有两个onResourceReady()方法,首先在第一个onResourceReady()方法当中,调用resource.get()方法获取到了封装的图片对象,也就是GlideBitmapDrawable对象,或者是GifDrawable对象。然后将这个值传入到了第二个onResourceReady()方法当中,并在第36行调用了target.onResourceReady()方法。
那么这个target又是什么呢?这个又需要向上翻很久了,在第三步into()方法的一开始,我们就分析了在into()方法的最后一行,调用了glide.buildImageViewTarget()方法来构建出一个Target,而这个Target就是一个GlideDrawableImageViewTarget对象。
那么我们去看GlideDrawableImageViewTarget的源码就可以了,如下所示:
public class GlideDrawableImageViewTarget extends ImageViewTarget {
private static final float SQUARE_RATIO_MARGIN = 0.05f;
private int maxLoopCount;
private GlideDrawable resource;
public GlideDrawableImageViewTarget(ImageView view) {
this(view, GlideDrawable.LOOP_FOREVER);
}
public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
super(view);
this.maxLoopCount = maxLoopCount;
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
@Override
public void onStart() {
if (resource != null) {
resource.start();
}
}
@Override
public void onStop() {
if (resource != null) {
resource.stop();
}
}
}
在GlideDrawableImageViewTarget的onResourceReady()方法中做了一些逻辑处理,包括如果是GIF图片的话,就调用resource.start()方法开始播放图片,但是好像并没有看到哪里有将GlideDrawable显示到ImageView上的逻辑。
确实没有,不过父类里面有,这里在第25行调用了super.onResourceReady()方法,GlideDrawableImageViewTarget的父类是ImageViewTarget,我们来看下它的代码吧:
public abstract class ImageViewTarget extends ViewTarget implements GlideAnimation.ViewAdapter {
...
@Override
public void onResourceReady(Z resource, GlideAnimation super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
}
可以看到,在ImageViewTarget的onResourceReady()方法当中调用了setResource()方法,而ImageViewTarget的setResource()方法是一个抽象方法,具体的实现还是在子类那边实现的。
那子类的setResource()方法是怎么实现的呢?回头再来看一下GlideDrawableImageViewTarget的setResource()方法,没错,调用的view.setImageDrawable()方法,而这个view就是ImageView。代码执行到这里,图片终于也就显示出来了。
那么,我们对Glide执行流程的源码分析,到这里也终于结束了。
总结
真是好长的一篇文章,这也可能是我目前所写过的最长的一篇文章了。如果你之前没有读过Glide的源码,真的很难相信,这短短一行代码:
Glide.with(this).load(url).into(imageView);
背后竟然蕴藏着如此极其复杂的逻辑吧?
不过Glide也并不是有意要将代码写得如此复杂,实在是因为Glide的功能太强大了,而上述代码只是使用了Glide最最基本的功能而已。
现在通过两篇文章,我们已经掌握了Glide的基本用法,并且通过阅读源码了解了Glide总的执行流程。接下来的几篇文章,我会带大家深入到Glide源码的某一处细节,学习Glide更多的高级使用技巧,感兴趣的朋友请继续阅读下一章
————————————————
版权声明:本文为CSDN博主「guolin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guolin_blog/article/details/53939176
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680