前言
本文将针对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设置了图片,图片加载过程结束。