〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(三)
之前我要分析Glide的with()方法以及load()方法的内部逻辑(参阅:〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一),〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(二)),本篇主要分析into()方法。Glide.with().load()方法最终返回值类型为DrawableTypeRequest<ModelType>,而DrawableTypeRequest<ModelType>又继承于DrawableRequestBuilder<ModelType>,DrawableRequestBuilder<ModelType>又继承于GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>,因此本文分析的源码主要集中于这三个类。
那么问题来了,在Glide的into()方法中依次做了哪些操作呢?
划重点:
1.构建Target(什么是Target?请参阅前文)
2.创建Request,传入之前创建的Target
3.执行Request,计算图片宽高
4.加载及解析图片
一、构建Target
直接追踪into()方法源码。
DrawableRequestBuilder.java
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
最终返回值为Target。进入父类GenericRequestBuilder继续追踪源码。
GenericRequestBuilder.java
public Target<TranscodeType> 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;
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
因为Android中所有的UI操作皆在主线程,因此方法体内部首先判断into()方法是否在主线程执行(调用Util.assertMainThread()方法)。接下来根据目标view的ScaleType进行图片裁切(Transform。什么是Transform?请参阅前文),主要根据不同情况调用了applyCenterCrop()和applyFitCenter()方法。追踪一下这两个方法的内部逻辑。在GenericRequestBuilder类这两个方法为空实现,因此我们追踪到它的子类BitmapRequestBuilder类中,发现调用的方法为 fitCenter()和centerCrop(),继续追踪这两个方法内的源码,如下文。
BitmapRequestBuilder.java
......省略
@Override
void applyFitCenter() {
fitCenter();
}
......省略
@Override
void applyCenterCrop() {
centerCrop();
}
......省略
public BitmapRequestBuilder<ModelType, TranscodeType> fitCenter() {
return transform(glide.getBitmapFitCenter());
}
......省略
public BitmapRequestBuilder<ModelType, TranscodeType> centerCrop() {
return transform(glide.getBitmapCenterCrop());
}
......省略
public BitmapRequestBuilder<ModelType, TranscodeType> transform(BitmapTransformation... transformations) {
super.transform(transformations);
return this;
}
......省略
通过一系列的追踪,发现fitCenter()和centerCrop()最终调用了transform(BitmapTransformation... transformations)方法。这里提及一下transform()方法的参数,以上文源码 transform(glide.getBitmapFitCenter())传入的glide.getBitmapFitCenter()为例:
glide.getBitmapFitCenter()最终返回了Glide类的成员变量bitmapCenterCrop,这是一个CenterCrop的实例。
Glide.java
private final CenterCrop bitmapCenterCrop;
......省略
CenterCrop getBitmapCenterCrop() {
return bitmapCenterCrop;
}
......省略
而CenterCrop类最终实现了Transformation<T>接口,定义了裁切转化(Transform)的逻辑,那么真正执行居中裁切(centerCrop)的逻辑的代码在哪里呢?在CenterCrop类的transform()方法体中我们发现了端倪:
CenterCrop.java
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
toReuse.recycle();
}
return transformed;
}
注意到Transform()方法内部调用了TransformationUtils.centerCrop()。至此,我们在TransformationUtils类中找到了居中裁切(centerCrop)的真正实现逻辑:
TransformationUtils.java
public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
if (toCrop == null) {
return null;
} else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {
return toCrop;
}
// From ImageView/Bitmap.createScaledBitmap.
final float scale;
float dx = 0, dy = 0;
Matrix m = new Matrix();
if (toCrop.getWidth() * height > width * toCrop.getHeight()) {
scale = (float) height / (float) toCrop.getHeight();
dx = (width - toCrop.getWidth() * scale) * 0.5f;
} else {
scale = (float) width / (float) toCrop.getWidth();
dy = (height - toCrop.getHeight() * scale) * 0.5f;
}
m.setScale(scale, scale);
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
final Bitmap result;
if (recycled != null) {
result = recycled;
} else {
result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
}
// We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
TransformationUtils.setAlpha(toCrop, result);
Canvas canvas = new Canvas(result);
Paint paint = new Paint(PAINT_FLAGS);
canvas.drawBitmap(toCrop, m, paint);
return result;
}
这里的逻辑交给读者自行分析,毕竟不是Glide的主干逻辑,暂且略过。
我们回到into()方法的分析中。在into()方法中,最终返回了 into(glide.buildImageViewTarget(view, transcodeClass)),这里的buildImageViewTarget()方法是构建Target实例的核心,让我们看一下方法的逻辑实现:
Glide.java
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
这里的imageViewTargetFactory即ImageViewTarget的工厂类,负责创建ImageViewTarget实例,最终在ImageViewTargetFactory类中找到构建Target实例的逻辑:
ImageViewTargetFactory.java
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else 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)");
}
}
}
注:isAssignableFrom()方法用于判断Class对象所表示的类或接口与指定的Class参数所表示的类或接口是否相同。若B extends A,或 B == A,则A.isAssignableFrom(B)返回true,反之,返回false。
可以发现buildTarget(ImageView view, Class<Z> clazz)方法根据传入不同的clazz对象来构建不同类型的ImageViewTarget对象。那么这个clazz对象是从哪里传进来的呢?读者可以试着从源码中追踪看看,这里两行哥直接告诉答案:在第二篇中我们分析load()方法,其返回值类型为DrawableTypeRequest<ModelType>,而在DrawableTypeRequest<ModelType>中有两个关键方法asBitmap()和asGif():
DrawableTypeRequest.java
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}
可以发现这两个方法中分别创建了BitmapTypeRequest实例和GifTypeRequest实例。在创建这两种实例的时候,分别传入了Bitmap.class和GifDrawable.class(GifDrawable继承于GlideDrawable),这就是buildTarget(ImageView view, Class<Z> clazz)中传入的clazz。如果用户既没有调用asBitmap()也没有调用asGif(),通过源码可以发现默认传入了Bitmap.class,这里不再展开分析。
分析到这里,总结一下,调用Glide.with().load().asBitmap()、Glide.with().load().asGif()或没有调用asBitmap()和asGif(),最终会为buildTarget()方法传入两种不同的clazz。根据传入的clazz,buildTarget()方法中的if...else分支最终的返回值有两种:GlideDrawableImageViewTarget实例或BitmapImageViewTarget实例,而DrawableImageViewTarget很少用到,暂不考虑。
接着分析一下buildTarget()方法返回值Target到底是什么,追踪一下Target类的源码:
Target.java
public interface Target<R> extends LifecycleListener {
int SIZE_ORIGINAL = Integer.MIN_VALUE;
void onLoadStarted(Drawable placeholder);
void onLoadFailed(Exception e, Drawable errorDrawable);
void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation);
void onLoadCleared(Drawable placeholder);
void getSize(SizeReadyCallback cb);
void setRequest(Request request);
Request getRequest();
}
LifecycleListener.java
public interface LifecycleListener {
void onStart();
void onStop();
void onDestroy();
}
Target接口继承了LifecycleListener接口。LifecycleListener接口应该熟悉了,在第一篇里我们已经讲解过,之所以Glide可以根据目标Activity或Fragment的生命周期执行对应的操作,是因为RequestManagerFragment的成员变量lifecycle的成员变量Set<LifecycleListener> lifecycleListeners中的每个LifecycleListener对象实现了生命周期的监听(成员变量的成员变量?有点绕......建议读者回看第一篇的源码)。
在Target接口中定义了更多的监听回调,有多种实现。之前我们讲解过,Target是对需要展示图片的目标ImageView的封装,我们以Target接口的具体实现类ImageViewTarget为例进行分析(ViewTarget实现了Target接口):
ImageViewTarget.java
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
public ImageViewTarget(ImageView view) {
super(view);
}
@Override
public Drawable getCurrentDrawable() {
return view.getDrawable();
}
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
@Override
public void onLoadStarted(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setImageDrawable(errorDrawable);
}
@Override
public void onLoadCleared(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@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);
}
在上文的源码中,实现一些逻辑,比如在加载中显示占位图(onLoadStarted(Drawable placeholder)),在加载失败的时候显示错误图片(onLoadFailed(Exception e, Drawable errorDrawable))等等,源码的最后一行是抽象方法setResource(Z resource)。为什么要定义为抽象方法呢?因为Glide显示的图片可能有不同的来源,因此针对不同来源的图片,定义子类重新覆写该方法即可。以BitmapImageViewTarget为例,看看如何实现这个抽象方法的。
BitmapImageViewTarget.java
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
public BitmapImageViewTarget(ImageView view) {
super(view);
}
/**
* Sets the {@link android.graphics.Bitmap} on the view using
* {@link android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.
*
* @param resource The bitmap to display.
*/
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
}
很简单,仅仅是调用了ImageView.setImageBitmap(resource)方法展示了图片。至此,构建Target逻辑完成。
二、创建Request,传入之前创建的Target
这里的Request即图片加载请求。看到这里,读者可能产生了混乱,先梳理一下。
1.Glide.with().load()方法最终返回值类型为DrawableTypeRequest<ModelType>;
2.DrawableTypeRequest<ModelType>继承于DrawableRequestBuilder<ModelType>;
3.DrawableRequestBuilder<ModelType>继承于GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>;
4.GenericRequest<A, T, Z, R>实现了Request接口;
5.GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>是对GenericRequest<A, T, Z, R>的创建者模式包装。
这里面类关联略微复杂,需要读者体味一下。接下来,回头去看上文提及的into()方法:
GenericRequestBuilder.java
public Target<TranscodeType> 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));
}
方法体最后一行return语句调用了into(glide.buildImageViewTarget(view, transcodeClass)),进入源码看看执行了哪些操作。
GenericRequestBuilder.java
public <Y extends Target<TranscodeType>> 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;
}
方法体第一行同样对当前线程是否为主线程进行了判断。继续向下看,不知道大家是否还记得ListView的Adapter用法,为了重复利用ItemView,用viewHolder承载view。第一次创建convertView的时候,调用了convertView.setTag(viewHolder),下次复用convertView的时候,通过convertView.getTag()来获取原本的viewHolder,即将convertView通过setTag()方法与viewHolder一一绑定。这里利用了相似的原理,将target与request进行了一一绑定。首先,调用了target.getRequest()来获取曾经绑定的上一个request对象previous,如果previous存在,则销毁previous。然后通过buildRequest()方法重新创建一个request,并与target进行绑定。
看看target.getRequest()方法内部的逻辑。
ViewTarget.java
public Request getRequest() {
Object tag = getTag();
Request request = null;
if (tag != null) {
if (tag instanceof Request) {
request = (Request) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
}
}
return request;
}
......省略
@Override
public void setRequest(Request request) {
setTag(request);
}
......省略
private void setTag(Object tag) {
if (tagId == null) {
isTagUsedAtLeastOnce = true;
view.setTag(tag);
} else {
view.setTag(tagId, tag);
}
}
......省略
private Object getTag() {
if (tagId == null) {
return view.getTag();
} else {
return view.getTag(tagId);
}
}
摘取ViewTarget类中的四个方法getRequest()、setRequest()、setTag()和getTag()方法。之前一直在说,Target是对ImageView的封装,可以发现这两个方法的本质就是通过imageView.getTag()获取request以及通过imageView.setTag(request)绑定request。
回到上面的into(Y target)方法,继续分析。若target.getRequest()获取到的Request对象不为null(Target已经与一个图片请求相绑定),执行的操作是取消已经绑定的Request对象。previous.clear()主要逻辑是取消加载图片请求previous,requestTracker.removeRequest(previous)主要逻辑是从requestTracker(请求追踪器)对象内部维护的两个请求集合(所有请求集合和等待中请求集合)中移除加载图片的请求previous,previous.recycle()主要逻辑是将previous的成员变量置为null或默认值,如下文源码所示。
GenericRequest.java
@Override
public void clear() {
Util.assertMainThread();
if (status == Status.CLEARED) {
return;
}
cancel();
// Resource must be released before canNotifyStatusChanged is called.
if (resource != null) {
releaseResource(resource);
}
if (canNotifyStatusChanged()) {
target.onLoadCleared(getPlaceholderDrawable());
}
// Must be after cancel().
status = Status.CLEARED;
}
void cancel() {
status = Status.CANCELLED;
if (loadStatus != null) {
loadStatus.cancel();
loadStatus = null;
}
}
@Override
public void recycle() {
loadProvider = null;
model = null;
context = null;
target = null;
placeholderDrawable = null;
errorDrawable = null;
fallbackDrawable = null;
requestListener = null;
requestCoordinator = null;
transformation = null;
animationFactory = null;
loadedFromMemoryCache = false;
loadStatus = null;
REQUEST_POOL.offer(this);
}
RequestTracker.java
private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());//所有请求集合
private final List<Request> pendingRequests = new ArrayList<Request>();//等待中请求集合
public void removeRequest(Request request) {
requests.remove(request);
pendingRequests.remove(request);
}
稍微提一下recycle() 方法,该方法最后一行调用了REQUEST_POOL.offer(this)将该请求对象加入请求池队列以备复用。
继续回到into()方法,在销毁了上一个Request后,执行buildRequest(target)方法来创建新的Request,看一下实现逻辑:
GenericRequestBuilder.java
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> 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<TranscodeType> 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(Target<TranscodeType> target) 方法内最终执行了buildRequestRecursive(target, null)。buildRequestRecursive()方法比较复杂,不过前面绝大部分语句都是与缩略图有关的逻辑,跳过不看,只看最后一个else分支:return obtainRequest(target, sizeMultiplier, priority, parentCoordinator)。跟进obtainRequest()方法查看实现逻辑,执行了GenericRequest.obtain()方法并传入了大量参数,查看一下这个方法的源码:
GenericRequest.java
public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(
LoadProvider<A, T, Z, R> loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target<R> 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<Z> transformation,
Class<R> transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory<R> animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
if (request == null) {
request = new GenericRequest<A, T, Z, R>();
}
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;
}
private void init(
LoadProvider<A, T, Z, R> loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target<R> 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<Z> transformation,
Class<R> transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory<R> animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
this.loadProvider = loadProvider;
this.model = model;
this.signature = signature;
this.fallbackDrawable = fallbackDrawable;
this.fallbackResourceId = fallbackResourceId;
this.context = context.getApplicationContext();
this.priority = priority;
this.target = target;
this.sizeMultiplier = sizeMultiplier;
this.placeholderDrawable = placeholderDrawable;
this.placeholderResourceId = placeholderResourceId;
this.errorDrawable = errorDrawable;
this.errorResourceId = errorResourceId;
this.requestListener = requestListener;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.transformation = transformation;
this.transcodeClass = transcodeClass;
this.isMemoryCacheable = isMemoryCacheable;
this.animationFactory = animationFactory;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.diskCacheStrategy = diskCacheStrategy;
status = Status.PENDING;
// We allow null models by just setting an error drawable. Null models will always have empty providers, we
// simply skip our sanity checks in that unusual case.
if (model != null) {
check("ModelLoader", loadProvider.getModelLoader(), "try .using(ModelLoader)");
check("Transcoder", loadProvider.getTranscoder(), "try .as*(Class).transcode(ResourceTranscoder)");
check("Transformation", transformation, "try .transform(UnitTransformation.get())");
if (diskCacheStrategy.cacheSource()) {
check("SourceEncoder", loadProvider.getSourceEncoder(),
"try .sourceEncoder(Encoder) or .diskCacheStrategy(NONE/RESULT)");
} else {
check("SourceDecoder", loadProvider.getSourceDecoder(),
"try .decoder/.imageDecoder/.videoDecoder(ResourceDecoder) or .diskCacheStrategy(ALL/SOURCE)");
}
if (diskCacheStrategy.cacheSource() || diskCacheStrategy.cacheResult()) {
// TODO if(resourceClass.isAssignableFrom(InputStream.class) it is possible to wrap sourceDecoder
// and use it instead of cacheDecoder: new FileToStreamDecoder<Z>(sourceDecoder)
// in that case this shouldn't throw
check("CacheDecoder", loadProvider.getCacheDecoder(),
"try .cacheDecoder(ResouceDecoder) or .diskCacheStrategy(NONE)");
}
if (diskCacheStrategy.cacheResult()) {
check("Encoder", loadProvider.getEncoder(),
"try .encode(ResourceEncoder) or .diskCacheStrategy(NONE/SOURCE)");
}
}
}
看着代码特别长,其实很简单,就是创建了Request对象,并调用request.init()方法将大量的参数进行初始化赋值。留意一下obtain()方法体第一行,调用 (GenericRequest<A, T, Z, R>)REQUEST_POOL.poll()来获取请求池队列中曾经缓存的Request请求,如果获取到的结果为null,则直接通过构造方法创建一个GenericRequest对象。如果获取到的Request请求不为null,则复用。至此,创建Request的逻辑分析完毕。
三、执行Request,计算图片宽高
Request对象创建好了之后,到底是如何执行的呢?回到之前的into()方法。
GenericRequestBuilder.java
public <Y extends Target<TranscodeType>> 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;
}
倒数第二行requestTracker.runRequest(request)执行了创建好的Request对象,看看RequestTracker类runRequest()方法做了哪些操作。
RequestTracker.java
private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());//所有请求集合
private final List<Request> pendingRequests = new ArrayList<Request>();//等待中请求集合
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
首先将request加入到所有请求集合requests中。如果当前RequestTracker处于暂停状态,则将request加入到等待中请求集合pendingRequests中,如果当前Request不处于暂停状态,则调用begin()方法开始执行请求。让我们继续追踪begin()方法内部的逻辑。
GenericRequest.java
@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));
}
}
这里主要看一下如下核心代码:
GenericRequest.java
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
用户在调用override(width,height)时对overrideWidth与overrideHeight进行赋值(设置了固定的宽高),其初始值为0,Util.isValidDimensions()是用于校验是否是有效的覆写宽高,如果返回值为true则调用onSizeReady(overrideWidth, overrideHeight),否则调用 target.getSize()来获取图片的实际宽高。先来看看getSize()中做了哪些操作:
ViewTarget.java
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} else {
// We want to notify callbacks in the order they were added and we only expect one or two callbacks to
// be added a time, so a List is a reasonable choice.
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}
看源码逻辑,首先调用getViewWidthOrParam()和getViewHeightOrParam()获取图片的宽高,通常情况下是可以获取到有效的宽高值(当UI绘制完毕),直接回调onSizeReady(currentWidth, currentHeight)。但是如果在UI没有绘制完毕,获取到的宽高可能为int常量PENDING_SIZE,即0。这时就会走到第一个else分支,获取了View的viewThreeObserver,并为此添加了监听addOnPreDrawListener(SizeDeterminerLayoutListener layoutListener)。接下来看看SizeDeterminerLayoutListener到底做了哪些监听。
ViewTarget.java
private static class SizeDeterminerLayoutListener implements ViewTreeObserver.OnPreDrawListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<SizeDeterminer>(sizeDeterminer);
}
@Override
public boolean onPreDraw() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
}
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
可以看到SizeDeterminerLayoutListener对象持有了弱引用sizeDeterminer对象,便于GC在不需要的时候进行回收。重点看一下sizeDeterminer.checkCurrentDimens()逻辑。
ViewTarget.java
private void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight)) {
return;
}
notifyCbs(currentWidth, currentHeight);
ViewTreeObserver observer = view.getViewTreeObserver();
if (observer.isAlive()) {
observer.removeOnPreDrawListener(layoutListener);
}
layoutListener = null;
}
private void notifyCbs(int width, int height) {
for (SizeReadyCallback cb : cbs) {
cb.onSizeReady(width, height);
}
cbs.clear();
}
可以看到在获取到宽高后,执行了notifyCbs(currentWidth,currentHeight)来回调有效宽高,最终同上文一样,还是调用了onSizeReady()方法。也就是说不管走到begin()方法内部的哪个分支,最终都会回调onSizeReady()方法。
四、加载及解析图片
上文讲到,无论何种情况,都会走到onSizeReady()方法。进入onSizeReady()方法跟进源码,为了方便阅读,这里只保留核心代码:
GenericRequest.java
@Override
public void onSizeReady(int width, int height) {
......省略
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
......省略
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this);
......省略
}
这里看到了熟悉的对象:ModelLoader和ResourceTranscoder(不熟悉?请参阅第一篇)。这里主要的逻辑是通过loadProvider.getModelLoader()方法获取到ModelLoader实例和loadProvider.getTranscoder()方法获取到ResourceTranscoder实例。那么loadProvider又是什么对象呢?追踪一下源码,回到BitmapTypeRequest类中。
BitmapTypeRequest.java
BitmapTypeRequest(GenericRequestBuilder<ModelType, ?, ?, ?> other,
ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader,
RequestManager.OptionsApplier optionsApplier) {
super(buildProvider(other.glide, streamModelLoader, fileDescriptorModelLoader, Bitmap.class, null),
Bitmap.class, other);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.glide = other.glide;
this.optionsApplier = optionsApplier;
}
在BitmapTypeRequest的构造方法中,调用了buildProvider()方法创建了LoadProvider对象,看一下方法内部的逻辑:
private static <A, R> FixedLoadProvider<A, ImageVideoWrapper, Bitmap, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader,
Class<R> transcodedClass, ResourceTranscoder<Bitmap, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(Bitmap.class, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Bitmap> loadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
Bitmap.class);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Bitmap, R>(modelLoader, transcoder, loadProvider);
}
看最后一行,在创建FixedLoadProvider实例的构造方法中传入了modelLoader(在modelLoader的构造方法中传入了流ModelLoader:streamModelLoader和文档描述符ModelLoader:fileDescriptorModelLoader)、transcoder、loadProvider对象,再看看FixedLoadProvider类中查一下源码:
FixedLoadProvider.java
public class FixedLoadProvider<A, T, Z, R> implements LoadProvider<A, T, Z, R> {
private final ModelLoader<A, T> modelLoader;
private final ResourceTranscoder<Z, R> transcoder;
private final DataLoadProvider<T, Z> dataLoadProvider;
public FixedLoadProvider(ModelLoader<A, T> modelLoader, ResourceTranscoder<Z, R> transcoder,
DataLoadProvider<T, Z> dataLoadProvider) {
if (modelLoader == null) {
throw new NullPointerException("ModelLoader must not be null");
}
this.modelLoader = modelLoader;
if (transcoder == null) {
throw new NullPointerException("Transcoder must not be null");
}
this.transcoder = transcoder;
if (dataLoadProvider == null) {
throw new NullPointerException("DataLoadProvider must not be null");
}
this.dataLoadProvider = dataLoadProvider;
}
}
看到这里已经明白了,LoadProvider其实就是对ModelLoader、ResourceTranscoder、DataLoadProvider的封装,持有了这三种类型的成员变量,便于统一管理。
现在回到onSizeReady()方法,分析一下通过LoadProvider获取到的ModelLoader实例和ResourceTranscoder实例的主要作用。前文说过,ModelLoader实例负责将源数据(Model)转化为输入流(Data),往细了说,ModelLoader实例中具体承担此项工作的就是DataFetcher实例。DataFetcher是一个接口,有多种实现类,如下图所示:
DataFetcher实现类
之所以有这么多种实现类,是因为有多种Model,需要将其转化为Data就需要通过不同的手段,比如从Asset文件(AssetPathFetcher)、从File文件(FileDescriptorLocalUriFetcher)、从网络(HttUrlFetcher)等。
现在我们以ModelLoader的具体实现子类HttpUrlGlideUrlLoader为例,可以看到getResourceFetcher()方法返回了一个HttpUrlFetcher实例:
HttpUrlGlideUrlLoader.java
public class HttpUrlGlideUrlLoader implements StreamModelLoader<GlideUrl> {
private final ModelCache<GlideUrl, GlideUrl> modelCache;
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
return new HttpUrlFetcher(url);
}
}
看到这里可能有疑问,DataFetcher是如何将Model转化为Data(即输入流)呢?还是以HttpUrlFetcher为例,追踪一下源码:
HttpUrlFetcher.java
@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<String, String> 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<String, String> 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;
}
上文摘录了HttpUrlFetcher类中获取输入流的核心方法loadData()。虽然在onSizeReady()方法中并没有直接调用HttpUrlFetcher实例的loadData()方法(在另外的方法中调用了),但是我们可以先分析一下loadData()的逻辑。在loadData()内部调用了loadDataWithRedirects()方法。可以看到loadDataWithRedirects()方法内部创建了urlConnection并调用connect(),如果返回的状态码为200,则调用getStreamForSuccessfulRequest()方法来取得的InputStream,如果返回的状态码为300,则获取重定向url,重新调用loadDataWithRedirects()方法。至此DataFetcher类中Model转Data(输入流)的逻辑已经分析完毕。
回到onSizeReady()方法,这里通过loadProvider.getTranscoder()获取的transcoder作用是将DataFetcher获取到的Data(输入流)转为Resource。终于看见onSizeReady()中的核心方法engine.load()方法了。engine是Engine类的实例,Engine类是管理图片获取及加载任务的类。可以看到在engine.load()方法中传入了上文已经分析过的DataFetcher实例、LoadProvider实例以及ResourceTransCoder实例。那么engine.load()这个核心方法内部到底做了什么操作呢?下篇文章再做分析。