前言
本文将针对Glide的图片加载过程,从对最简单的Glide.with(this).load(url).into(imageView)出发,对Glide的源码进行分析,以下代码基于Glide-4.9.0。
源码分析
1 | Glide.with(this).load(url).into(imageView); |
这是Glide的最简单使用,通过这条语句就可以将网络图片加载到ImageView上。下面就来分析一下这个过程,首先是with方法:
Glide#with
1 | public static RequestManager with(@NonNull Context context) { |
with有多个重载方法,最终都是为了得到传入参数的上下文。继续看getRetriever方法:
Glide#getRetriever
1 |
|
get方法可以获得Glide实例,实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
这里使用volatile加DCL实现了单例模式,所以Glide是一个单例。最终Glide在GlideBuilder的build方法中创建。
继续看getRequestManagerRetriever方法:1
2
3public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
这里返回一个RequestManagerRetriever对象。该对象是在GlideBuilder的build方法中初始化的。最终,Glide的getRetriever方法返回的是一个RequestManagerRetriever对象。回到with方法,获得RequestManagerRetriever后又调用了它的get方法:
RequestManagerRetriever#get
1 | public RequestManager get(@NonNull Context context) { |
该方法获取RequestManager,如果是在主线程调用Glide.with方法,且不是Application的context,会调用get方法获取并传入对应类型的context,否则调用getApplicationManager方法获取。
如果是在主线程并且context不是Application,最终都会调用这两个方法之一获取RequestManager:1
2
3
4
5RequestManager supportFragmentGet(Context context, FragmentManager fm,
Fragment parentHint, boolean isParentVisible);
RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
android.app.Fragment parentHint, boolean isParentVisible)
这两个方法实现的原理是一样的,之所以分两个方法是为了兼容support包中的FragmentActivity、Fragment。
分析其中一个方法,假如传入的Context是Acticity,并且该Activity未被销毁,就调用fragmentGet方法,其实现如下:
RequestManagerRetriever#fragmentGet
1 | private RequestManager fragmentGet(@NonNull Context context, |
可以看到,在该方法中会创建一个没有界面的Fragment(RequestManagerFragment)并添加到当前Activity。如果RequestManagerFragment还没绑定RequestManager的话,就会创建一个RequestManager并绑定到RequestManagerFragment中。
通过RequestManager就可以对Activity的生命周期进行监听。
如果是在子线程,或者context是Application的话,就会调用getApplicationManager方法获取RequestManager。其实现如下:
RequestManagerRetriever#getApplicationManager
1 | private volatile RequestManager applicationManager; |
可以看到,这里用单例模式创建了RequestManager。
小结
with方法执行步骤
- 如果还没有Glide实例,先通过单例模式创建一个Glide,然后获得RequestManagerRetriever。
- 接着通过RequestManagerRetriever的get方法获取到RequestManager。这里分两种情况:
- 如果此时是在主线程并且context不是Application。那么就会根据context的类型,先创建一个RequestManagerFragment(没有界面的Fragment)并添加到对应的Activity或Fragment中。接着创建一个RequestManager并绑定到刚创建的RequestManagerFragment中,之后就可以通过该RequestManager对Activity或Fragment的生命周期进行监听。
- 如果是在子线程,或者context是Application的话,就会调用getApplicationManager方法获取一个唯一的RequestManager,该RequestManager会对Application的生命周期进行监听。
一些结论
- 一个Activity或一个Fragment对应一个RequestManagerFragment,一个RequestManagerFragment对应一个RequestManager,所以一个Activity或一个Fragment对应一个RequestManager,该RequestManager可以监听对应Activity或Fragment的生命周期。
- 如果在Fragment以及其所属的Activity中分别调用with方法创建Glide请求,并不会只创建一个RequestManager,该Fragment和Activity分别有各自的RequestManager。对于Fragment及其所属Fragment,与之同理。
- 在子线程发起Glide请求或者传入的context为Application,将使用全局唯一的RequestManager。(由于Glide是单例,所以Glide中的RequestManagerRetriever也是单例,而RequestManagerRetriever中的applicationManager对象也是采用单例模式,所以全局只有一个applicationManager对象)
RequestManager#load
Glide的with方法最终返回RequestManager,继续看RequestManager的load方法,load有一系列的重载方法: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
35public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
这些方法都先调用asDrawable方法:1
2
3public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
继续看as方法:1
2
3
4public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
最终返回一个RequestBuilder对象,调用RequestBuilder的相应load方法。这里只分析传入的类型为String类型的情况。
RequestBuilder#load
1 | public RequestBuilder<TranscodeType> load(@Nullable String string) { |
先看loadGeneric方法:1
2
3
4
5private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
该方法将不同的参数类型统一赋给一个Object类型的model成员变量。如果是Bitmap或Drawable类型,将会额外设置为不做缓存:1
2
3
4public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
最后,方法返回自身,即返回RequestBuilder。
小结
load方法的执行步骤
RequestManager将load过程交由RequestBuilder来处理,RequestBuilder将不同的参数类型统一赋给一个Object类型的model成员变量,如果参数是Bitmap或Drawable类型,还会额外设置为不做缓存。最后,返回RequestBuilder本身。
一些结论
- RequestBuilder使用了Builder模式,通过load方法返回自身后,还可以继续链式调用自身的方法设置RequestOption、加载失败占位图等。
RequestBuilder#into
load方法返回RequestBuilder,现在看RequestBuilder的into方法,它暴露的重载方法有3个,其中一个被弃用,剩下两个如下:1
2
3<Y extends Target<TranscodeType>> Y into(@NonNull Y target);
ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view);
其中一个参数是Target对象,可以定制化一个target并返回。另外一个参数是ImageView,作用是指定图片最后要加载到的位置。这里分析传入参数为ImageView的情况: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 public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
//检查是否额外设置了ImageView的裁剪
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
//根据传入的ImageView构建Target
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
//创建一个主线程的Executor,里面封装了运行在主线程的Handler
Executors.mainThreadExecutor());
}
在该方法中,首先检查是否额外设置了ImageView的裁剪,如果有就加到requestOptions中。然后根据传入的ImageView构建Target,最后调用另一个私有的into方法进行后续操作。
构建Target的过程看GlideContext的buildImageViewTarget的方法:1
2
3
4public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
继续看ImageViewTargetFactory的buildTarget方法:1
2
3
4
5
6
7
8
9
10
11public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
这里根据传入的class类型构建不同的类型的Target,现在传入的是Drawable.class,所以创建的是DrawableImageViewTarget1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
//...
//刚方法负责将获得的Drawable资源加载到指定的ImageView上
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
现在回到load方法,继续看其最后调用的另一个load重载方法: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 private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//构建Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//获取当前target已绑定的Request
Request previous = target.getRequest();
//如果新创建的Request和当前target已绑定的Request相同,直接复用已绑定的Request
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//回收新创建的Request
request.recycle();
//如果已绑定的Request已经在运行,那就让它继续运行,不必再次请求。否则再次开始请求
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
//清除target之前绑定的Request
requestManager.clear(target);
//给target绑定新的Request
target.setRequest(request);
//通知RequestManager执行Request
requestManager.track(target, request);
return target;
}
该方法主要功能是创建和执行Request。创建Request的过程比较复杂,会先后创建ErrorRequestCoordinator、ThumbnailRequestCoordinator和SingleRequest。现在看一下执行Request的过程,首先看RequestManager的track方法:
RequestManager#track
1 | synchronized void track(@NonNull Target<?> target, @NonNull Request request) { |
该方法先将target加入到跟踪队列中,该队列保存了当前Activity或Fragment中所有的target,便于利用生命周期管理各个target的Request请求。之后通过RequestTracker的runRequest执行请求:1
2
3
4
5
6
7
8
9
10
11
12
13
14public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
//如果Request队列不处于暂停状态,执行Request
request.begin();
} else {
//否则清除资源并加入挂起队列
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
SingleRequest实现了Request接口,所以最终是通过SingleRequest的begin方法来执行Request。
小结
into方法的执行步骤
这里分析into方法传入参数为ImageView的情况:
- 首先检查是否额外设置了ImageView的裁剪,如果有就加到requestOptions中,然后根据传入的ImageView构建相应的Target,这里创建的是DrawableImageViewTarget。
- 根据target和requestOptions等参数构造Request,然后判断新创建的Request和target已绑定的Request是否相同:
- 如果相同,先回收新创建的Request,然后如果已绑定的Request已经在运行,那就让它继续运行,不必再次请求。否则再次发起请求。
- 如果不相同,先清除target之前绑定的Request然后绑定新的Request,之后通知RequestManager执行Request。RequestManager会先把该target加入到跟踪队列以便根据生命周期管理target的request,最后交给RequestTracker真正执行Request。
- 在执行Request的时候,先判断Request的状态,如果Request的状态为COMPLETE,可以直接利用上一次获取到的数据。否则会通过Engine的load方法从缓存或网络获取到数据,之后将数据加载进ImageView。
接下来详细看下Request的执行过程,从SingleRequest的begin方法开始看起:
SingleRequest#begin
1 |
|
该方法先进行一些判断:如果用户传入的参数为null则回调加载失败,如果Request正在运行则抛出异常。然后再判断之前是否完成过加载,如果是的话可以利用上一次获取的资源,调用onResourceReady并返回。
判断过后,真正开始加载:首先在确定宽高后调用onSizeReady方法加载数据,之后调用onLoadStarted设置占位图,最后在onResourceReady将数据装入ImageView或返回给target。
现在分别看下onSizeReady、onLoadStarted和onResourceReady:
SingleRequest#onSizeReady
onSizeReady方法涉及到了Glide的缓存机制,篇幅较长,故在另一篇文章中单独讲,详情请看Glide源码分析:缓存机制。
ImageViewTarget#onLoadStarted
onSizeReady方法结束后,Request的状态变为RUNNING。这时会调用Target的onLoadStarted方法,ImageViewTarget的该方法实现如下:1
2
3
4
5
6
7
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
//设置占位图
setDrawable(placeholder);
}
改方法用于设置占位图
SingleRequest#onResourceReady
无论在Engine的load方法中,是通过哪种方式获得图片数据的,最终如果获取成功都会回调SingleRequest的onResourceReady方法,并将图片的Resource传递过来。而onResourceReady方法会将获取到的数据加载进ImageView中或作为Target返回。
该方法的实现如下:1
2
3
4
5
6
7
8
9public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
//一些状态的清除
//如果资源为空,回调onLoadFailed方法加载错误占位图
Object received = resource.get();
//继续调用另一重载方法
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
继续看onResourceReady的另一重载方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
//...
try {
boolean anyListenerHandledUpdatingTarget = false;
//...
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
//将资源加载进target
target.onResourceReady(result, animation);
}
}
notifyLoadSuccess();
}
调用ImageViewTarget的onResourceReady加载资源:1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
//没有动画的话
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
setResource(resource);
maybeUpdateAnimatable(resource);
}
没有动画的话最终调用setResource方法,DrawableImageViewTarget的实现如下:1
2
3
4
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
可以看到,最终在该方法中给ImageView设置了图片,图片加载过程结束。