Glide学习记录(2)

本篇继续上次未完成的Glide学习记录。
上次说到了Glide的with()方法,Glide是如何实现图片加载结合组件声明周期的,这次继续讲解之后的方法。
glide通常的使用方式:

1
2
3
4
5
6
7
Glide
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);

这是来自Glide的官方示例。
下面我们来看一看load方法做了些什么。

1
2
3
4
5
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}

可以看到,调用with方法后返回了一个RequestManager实例,现在深入RequestManager内部看一看load方法。
在这之前首先来看一看RequestManager到底是个什么东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* A class for managing and starting requests for Glide. Can use activity, fragment and connectivity
* lifecycle events to intelligently stop, start, and restart requests. Retrieve either by
* instantiating a new object, or to take advantage built in Activity and Fragment lifecycle
* handling, use the static Glide.load methods with your Fragment or Activity.
*
* @see Glide#with(android.app.Activity)
* @see Glide#with(android.support.v4.app.FragmentActivity)
* @see Glide#with(android.app.Fragment)
* @see Glide#with(android.support.v4.app.Fragment)
* @see Glide#with(Context)
*/
public class RequestManager implements LifecycleListener

按照注释说明RequestManager是一个管理开启Request的类,这个可以智能的开启、停止、重新发起一个Request,我们可以看到这个类继承于LifecycleListener,在上一章的学习里我们已经知道了它的实例在with方法中被创建并被加入到子Fragment中,因而能实现智能的控制Request。
OK,接下来就去看load的具体内容。

1
2
3
4
5
6
7
8
9
/**
* A helper method equivalent to calling {@link #asDrawable()} and then {@link
* RequestBuilder#load(Object)} with the given model.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}

可以看到这是一个工具方法,它等效于两个方法:
(1)构造方法asDrawalbe()
(2)load(model)
一步一步深入这两个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Attempts to always load the resource using any registered {@link
* com.bumptech.glide.load.ResourceDecoder}s that can decode any subclass of {@link Drawable}.
*
* <p> By default, may return either a {@link android.graphics.drawable.BitmapDrawable} or {@link
* GifDrawable}, but if additional decoders are registered for other {@link Drawable} subclasses,
* any of those subclasses may also be returned. </p>
*
* @return A new request builder for loading a {@link Drawable}.
*/
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class).transition(new DrawableTransitionOptions());
}

按照注释,asDrawlbe类会尝试使用所有注册的ResourceDecoder去解码资源,这个ResourceDecoder可以解码任何Drawble的子类。在默认情况下,它会返回BitmapDrawble和GifDrawable两者中的一个,但是如果你手动注册额外的解码器,那么资源就可以被解码成Drawable的任何子类。
这里有几点需要说明:
首先,通过网络调用获取到的图片传到本机上时应该只是一组二进制序列(图片通常使用Base64传输),而解码器就是决定如何解析它的工具,Glide的解码器还决定它以什么实例返回。
可以看到,Glide给我们提供了充分的自由,这个ResourceDecoder接口可以被我们手动实现然后注册,只要我们需要的返回类的形式是Drawalbe的子类,那么它都可以满足。
接下来是ReuqestBuilder的load方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
* Sets the specific model to load data for.
*
* <p> This method must be called at least once before
* {@link #into(com.bumptech.glide.request.target.Target)} is called. </p>
*
* @param model The model to load data for, or null.
* @return This request builder.
*/
@SuppressWarnings("unchecked")
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}

这里使用了工厂模式,它的注释说了很多,但是其实这里没有做什么事情,只是把内部的model的设置成传入的model。

1
2
3
4
5
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}

到了这里load方法就结束了,通过之前的分析,load方法内部其实没有什么真正的业务,它通过RequestManager生成了一个RequestBuilder实例并对这个实例做了一些配置。
接下来我们先来看一看into方法,实际看一看从RequestBuilder到图片加载完成的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
* the view, and frees any resources Glide may have previously loaded into the view so they may be
* reused.
*
* @see RequestManager#clear(Target)
*
* @param view The view to cancel previous loads for and load the new resource into.
* @return The
* {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
*/

public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);

if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop(context);
break;
case CENTER_INSIDE:
requestOptions.optionalCenterInside(context);
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter(context);
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}

return into(context.buildImageViewTarget(view, transcodeClass));
}

先看注释,调用into方法后会取消之前所有的Request并且清除之前Glide加载进这个view的内,因此我们可以放心的多次调用into。
再看into方法的逻辑,这里它也没有做什么真正的逻辑,可以看到,它根据imageview的ScaleType去配置了requestBuilder中的requestOption,这个RequestOption之后我们再详细去看,现在先跳到下一步。
最后调用了重载的另一个into方法,它的参数是一个Target,这里调用了GlideContext里的一个方法生成了一个Target传入。
现在来看看参数为Target的into方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Set the target the resource will be loaded into.
*
* @param target The target to load the resource into.
* @return The given target.
* @see RequestManager#clear(Target)
*/

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}

Request previous = target.getRequest();

if (previous != null) {
requestManager.clear(target);
}

requestOptions.lock();
Request request = buildRequest(target);
target.setRequest(request);
requestManager.track(target, request);

return target;
}

这个方法挺容易理解的,首先判断是不是在主线程,如果不是就直接抛出异常,也就是说into方法只能在主线程被调用。接下来也是做一些检查,target是不是空、执行前有没有执行with()方法。然后清除target之前的request,最后调用lock方法对这个request上锁,不能再对request做任何的改动,Glide也没有提供直接解锁的方法,唯一的解锁方法就是调用clone再创建一个requestOption。这样设计的用意就是使request不能复用。
最后创建新的reuqest把target的request设置成当前request然后使用reuqestManager的track方法执行request。

1
2
3
4
void track(Target<?> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}

可以看到,真正执行网络操作的内容就在runRequest下面。

1
2
3
4
5
6
7
8
9
10
11
/**
* Starts tracking the given request.
*/

public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}

这里执行真正的加载逻辑,因为Request只是一个接口,现在要回头去找它的真正实现类。
我们回到上一个方法里找到创建request的方法。在上一个into方法里有一个buildRequest方法。

1
2
3
4
private Request buildRequest(Target<TranscodeType> target) {
return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(),
requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight());
}

这个方法只是一个中转方法,真正的实现在buildRequestRecursive里,接下来深入这个方法的内部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
private Request buildRequestRecursive(Target<TranscodeType> target,
@Nullable ThumbnailRequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority, int overrideWidth, int overrideHeight) {

if (thumbnailBuilder != null) {
// Recursive case: contains a potentially recursive thumbnail request builder.
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()");

}

TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
thumbnailBuilder.transitionOptions;
if (DEFAULT_ANIMATION_OPTIONS.equals(thumbTransitionOptions)) {
thumbTransitionOptions = transitionOptions;
}

Priority thumbPriority = thumbnailBuilder.requestOptions.isPrioritySet()
? thumbnailBuilder.requestOptions.getPriority() : getThumbnailPriority(priority);

int thumbOverrideWidth = thumbnailBuilder.requestOptions.getOverrideWidth();
int thumbOverrideHeight = thumbnailBuilder.requestOptions.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !thumbnailBuilder.requestOptions.isValidOverride()) {

thumbOverrideWidth = requestOptions.getOverrideWidth();
thumbOverrideHeight = requestOptions.getOverrideHeight();
}

ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, requestOptions, coordinator,
transitionOptions, priority, overrideWidth, overrideHeight);

isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailBuilder.buildRequestRecursive(target, coordinator,
thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight);

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, requestOptions, coordinator, transitionOptions,
priority, overrideWidth, overrideHeight);

BaseRequestOptions<?> thumbnailOptions = requestOptions.clone()
.sizeMultiplier(thumbSizeMultiplier);

Request thumbnailRequest = obtainRequest(target, thumbnailOptions, coordinator,
transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight);


coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority,
overrideWidth, overrideHeight);

}
}

前面那些复杂的逻辑先不看,直接跳到最后一行,可以看到reuqest的实现类是SingleRequest。
深入看一下这个类的逻辑

1
2
3
4
5
6
7
/**
* A {@link Request} that loads a {@link com.bumptech.glide.load.engine.Resource} into a given
* {@link Target}.
*
* @param <R> The type of the resource that will be transcoded from the loaded resource.
*/

public final class SingleRequest<R> implements Request,

这个类是是一个resource和一个request的结合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public void begin() {
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;
}

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));
}
}

这里就是刚才begin真正执行的内容。到了这里结果我们又发现真正加载的逻辑是在Target的方法里,而这个Target同样也是一个接口,现在得倒回去找Target的实现类。
重新倒回去into方法,最后可以找到创建Target的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* A factory responsible for producing the correct type of
* {@link com.bumptech.glide.request.target.Target} for a given {@link android.view.View} subclass.
*/

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)");
}
}
}

可以看到Target是分类的,和Request的单个实现不同。还记得我们之前说到的asDrawalbe方法吗,那个方法就是设置这个传入的class,最后生成的对应Target对应的就应该是DrawalbeImageViewTarget,这里可以看到还有另一种Target:BitmapImageViewTarget。具体有什么区别过会再说,先看DrawalbeImageViewTarget的内部实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* A target for display {@link Drawable} objects in {@link ImageView}s.
*/

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

public DrawableImageViewTarget(ImageView view) {
super(view);
}

@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}

没什么内容,看来实现还在父类里。
在它的父类里找到了这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param placeholder {@inheritDoc}
*/
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}

注释里说明了这里就仅仅是调用了view的setImageDrawable方法把图片设置进入。那么是什么时候得到这个Drawable的呢?看到这个方法的参数placeholder,我们再回头找它的调用者。
它的调用者正是singleRequest实例,现在转回去看这部分逻辑。

1
2
3
4
5
6
7
8
9
private Drawable getPlaceholderDrawable() {
if (placeholderDrawable == null) {
placeholderDrawable = requestOptions.getPlaceholderDrawable();
if (placeholderDrawable == null && requestOptions.getPlaceholderId() > 0) {
placeholderDrawable = loadDrawable(requestOptions.getPlaceholderId());
}
}
return placeholderDrawable;
}