Glide源码分析:图片加载过程

前言

本文将针对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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}

public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}

public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}

public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}

public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}

public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}

with有多个重载方法,最终都是为了得到传入参数的上下文。继续看getRetriever方法:

Glide#getRetriever

1
2
3
4
5
6
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
//确保context不为空

return Glide.get(context).getRequestManagerRetriever();
}

get方法可以获得Glide实例,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private 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
3
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}

这里返回一个RequestManagerRetriever对象。该对象是在GlideBuilder的build方法中初始化的。最终,Glide的getRetriever方法返回的是一个RequestManagerRetriever对象。回到with方法,获得RequestManagerRetriever后又调用了它的get方法:

RequestManagerRetriever#get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
}
//在主线程调用Glide.with方法,且不是Application的context,会调用get方法并传入对应类型的context
else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}

//如果是在子线程调用Glide.with方法,或者传入的是Application的context
return getApplicationManager(context);
}

该方法获取RequestManager,如果是在主线程调用Glide.with方法,且不是Application的context,会调用get方法获取并传入对应类型的context,否则调用getApplicationManager方法获取。

如果是在主线程并且context不是Application,最终都会调用这两个方法之一获取RequestManager:

1
2
3
4
5
RequestManager 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
//在当前Activity创建一个没有界面的Fragment并添加到当前Activity中
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//创建一个RequestManager
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
//将RequestManager和Fragment绑定
current.setRequestManager(requestManager);
}
return requestManager;

}

可以看到,在该方法中会创建一个没有界面的Fragment(RequestManagerFragment)并添加到当前Activity。如果RequestManagerFragment还没绑定RequestManager的话,就会创建一个RequestManager并绑定到RequestManagerFragment中。

通过RequestManager就可以对Activity的生命周期进行监听。

如果是在子线程,或者context是Application的话,就会调用getApplicationManager方法获取RequestManager。其实现如下:

RequestManagerRetriever#getApplicationManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private volatile RequestManager applicationManager;

private RequestManager getApplicationManager(@NonNull Context context) {

if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}

return applicationManager;
}

可以看到,这里用单例模式创建了RequestManager。

小结

with方法执行步骤

  1. 如果还没有Glide实例,先通过单例模式创建一个Glide,然后获得RequestManagerRetriever。
  2. 接着通过RequestManagerRetriever的get方法获取到RequestManager。这里分两种情况:
  • 如果此时是在主线程并且context不是Application。那么就会根据context的类型,先创建一个RequestManagerFragment(没有界面的Fragment)并添加到对应的Activity或Fragment中。接着创建一个RequestManager并绑定到刚创建的RequestManagerFragment中,之后就可以通过该RequestManager对Activity或Fragment的生命周期进行监听。
  • 如果是在子线程,或者context是Application的话,就会调用getApplicationManager方法获取一个唯一的RequestManager,该RequestManager会对Application的生命周期进行监听。

一些结论

  1. 一个Activity或一个Fragment对应一个RequestManagerFragment,一个RequestManagerFragment对应一个RequestManager,所以一个Activity或一个Fragment对应一个RequestManager,该RequestManager可以监听对应Activity或Fragment的生命周期。
  2. 如果在Fragment以及其所属的Activity中分别调用with方法创建Glide请求,并不会只创建一个RequestManager,该Fragment和Activity分别有各自的RequestManager。对于Fragment及其所属Fragment,与之同理。
  3. 在子线程发起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
35
public 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
3
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}

继续看as方法:

1
2
3
4
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}

最终返回一个RequestBuilder对象,调用RequestBuilder的相应load方法。这里只分析传入的类型为String类型的情况。

RequestBuilder#load

1
2
3
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}

先看loadGeneric方法:

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

该方法将不同的参数类型统一赋给一个Object类型的model成员变量。如果是Bitmap或Drawable类型,将会额外设置为不做缓存:

1
2
3
4
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}

最后,方法返回自身,即返回RequestBuilder。

小结

load方法的执行步骤

RequestManager将load过程交由RequestBuilder来处理,RequestBuilder将不同的参数类型统一赋给一个Object类型的model成员变量,如果参数是Bitmap或Drawable类型,还会额外设置为不做缓存。最后,返回RequestBuilder本身。

一些结论

  1. 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
4
public <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
11
public <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,所以创建的是DrawableImageViewTarget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

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

//...

//刚方法负责将获得的Drawable资源加载到指定的ImageView上
@Override
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
2
3
4
5
6
 synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
//将target加入到跟踪队列
targetTracker.track(target);
//执行Request
requestTracker.runRequest(request);
}

该方法先将target加入到跟踪队列中,该队列保存了当前Activity或Fragment中所有的target,便于利用生命周期管理各个target的Request请求。之后通过RequestTracker的runRequest执行请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public 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的情况:

  1. 首先检查是否额外设置了ImageView的裁剪,如果有就加到requestOptions中,然后根据传入的ImageView构建相应的Target,这里创建的是DrawableImageViewTarget。
  2. 根据target和requestOptions等参数构造Request,然后判断新创建的Request和target已绑定的Request是否相同:
  • 如果相同,先回收新创建的Request,然后如果已绑定的Request已经在运行,那就让它继续运行,不必再次请求。否则再次发起请求。
  • 如果不相同,先清除target之前绑定的Request然后绑定新的Request,之后通知RequestManager执行Request。RequestManager会先把该target加入到跟踪队列以便根据生命周期管理target的request,最后交给RequestTracker真正执行Request。
  1. 在执行Request的时候,先判断Request的状态,如果Request的状态为COMPLETE,可以直接利用上一次获取到的数据。否则会通过Engine的load方法从缓存或网络获取到数据,之后将数据加载进ImageView。

接下来详细看下Request的执行过程,从SingleRequest的begin方法开始看起:

SingleRequest#begin

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
 @Override
public synchronized void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();

//如果用户传入的参数为空
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}

//如果Request正在运行,抛出异常
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}

//如果在完成加载后重写请求加载,例如调用notifyDataSetChanged。可以利用上一次获取的资源。
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}

status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//确定宽高后调用onSizeReady
onSizeReady(overrideWidth, overrideHeight);
} else {
//获取长宽尺寸,获取完之后会调用onSizeReady
target.getSize(this);
}

if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//开始加载图片
target.onLoadStarted(getPlaceholderDrawable());
}

}

该方法先进行一些判断:如果用户传入的参数为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
@Override
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
9
public 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
23
private 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
@Override
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
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}

可以看到,最终在该方法中给ImageView设置了图片,图片加载过程结束。

参考

-------------    本文到此结束  感谢您的阅读    -------------
0%