Glide源码解析

487 阅读17分钟

1.png

作为Android平台使用最广泛的图片加载库,Glide具有快速高效、API易于使用、支持扩展等特点。

官方文档:muyangmin.github.io/glide-docs-…

Glide基本用法

Glide使用简明的流式语法API,通过一行代码就可以进行图片加载。本文也将从源码角度,分析with()load()into()三个函数的内部原理。

Glide.with(this).load(url).into(imageView)

三步流程解析

with()

绑定不同的生命周期

with()用来执行对Glide的初始化,以及绑定生命周期。它可以接收多种参数:

  • Context:将Glide与Application的生命周期绑定,意味着即使推出了正在加载图片的页面,加载任务仍然不会停止。不建议这么做。
  • Activity/FragmentActivity/Fragment:与相应页面绑定,当页面onPause()时,暂停请求,onStart()时恢复请求,onDestroy()时清空请求。
  • View:会使用View.getContext()得到的Activity/Fragment Context进行绑定,但不建议这么用,因为View未发生attach()时是没有Context的;同时,对于具有复杂hierarchy的视图结构,开销很大。

所有的with()函数都会走到getRetriever()中。

Glide.java

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 View view) {
  return getRetriever(view.getContext()).get(view);
}

创建Glide单例

retriever翻译为“猎犬、寻回器”,getRetriever()用来获取(新建or复用)一个RequestManager,它首先断言传入的Context非空,然后调用Glide.get().getRequestRetriever()

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

Glide.get()采用饿汉式单例模式,进行初始化,在APP生命周期内,使用同一个Glide单例。

单例背景知识:饱汉——等到调用时才初始化,饿汉——类加载即进行初始化

public static Glide get(@NonNull Context context) {
  if (glide == null) {
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context);
      }
    }
  }

  return glide;
}

初始化函数较长,已经把关键逻辑在注释中进行说明。

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
  Context applicationContext = context.getApplicationContext();
  // <--重点1:解析自定义注解的AppGlideModule,详见下文
  GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
  List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
  if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) { // 解析Manifest中的自定义Module
    manifestModules = new ManifestParser(applicationContext).parse();
  }

  // 使用@excludes注解解决自定义module冲突,因为一个APP进程只允许拥有一个module
  if (annotationGeneratedModule != null
      && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
    Set<Class<?>> excludedModuleClasses =
        annotationGeneratedModule.getExcludedModuleClasses();
    Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
    while (iterator.hasNext()) {
      com.bumptech.glide.module.GlideModule current = iterator.next();
      if (!excludedModuleClasses.contains(current.getClass())) {
        continue;
      }
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
      }
      iterator.remove();
    }
  }
  ...

  RequestManagerRetriever.RequestManagerFactory factory =
      annotationGeneratedModule != null
          ? annotationGeneratedModule.getRequestManagerFactory() : null;
  builder.setRequestManagerFactory(factory);
  // <--重点2:加载module中的自定义配置
  for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    module.applyOptions(applicationContext, builder);
  }
  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.applyOptions(applicationContext, builder);
  }
  Glide glide = builder.build(applicationContext);
  for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    module.registerComponents(applicationContext, glide, glide.registry);
  }
  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
  }
  applicationContext.registerComponentCallbacks(glide);
  Glide.glide = glide;
}

自定义AppGlideModule

APP可以自定义AppGlideModule,有两种方式:

  • 4.0之前,在AndroidManifest.xml文件里面定义
  • 4.0及以后,通过@GlideModule注解定义
public abstract class AppGlideModule extends LibraryGlideModule implements AppliesOptions {
  public boolean isManifestParsingEnabled() {
    return true;
  }

  @Override
  public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
    // Default empty impl.
  }
}

通过自定义applyOptions()函数,可以修改Glide默认的配置,比如Bitmap缓存池大小、调整Executor等。

Module冲突

由于Glide限制在一个APP进程内只允许有一个自定义AppGlideModule存在,如果引入的第三方库也定义了Module,就会发生冲突。此时使用@excluded注解来使其中一个无效。

例如,你想让第三方库定义的com.example.unwanted.GlideModule失效,使自己定义的MyAppGlideModule生效,可以这么写。

@Excludes(com.example.unwanted.GlideModule)
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }

也可以一次排除多个模块

@Excludes({com.example.unwanted.GlideModule, com.example.conflicing.GlideModule})
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }

getRetriever()

回到with()函数,完成Glide初始化后,调用getRequestManagerRetriever(),得到new出来的RequestManagerRetriever实例,接着调用get()获得请求管理器Retriever,这部分逻辑还挺有意思的,一起来看下。

RequestManagerRetriever.java

@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      // 主线程,继续调用对应的get函数
    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());
    }
  }
  // 子线程,返回ApplicationManager
  return getApplicationManager(context);
}

public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity); // 断言Activity未被销毁
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet( // <--重点:创建空Fragment
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we're on a background thread.
  // 1.使用ApplicationContext,2.在后台线程,这两种场景下会使用ApplicationManager
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        // Normally pause/resume is taken care of by the fragment we add to the fragment or
        // activity. However, in this case since the manager attached to the application will not
        // receive lifecycle events, we must force the manager to start resumed using
        // ApplicationLifecycle.
        Glide glide = Glide.get(context.getApplicationContext()); // get到之前创建的单例
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }

  return applicationManager; //  
}

【重点】空Fragment策略

Glide是如何把图片任务与Activity生命周期进行绑定的,以便于Activity停止时,中止图片加载,Activity销毁时,清空加载任务?答案就是,它创建了一个空Fragment,并通过宿主Activity的FragmentManager,将其加入到当前Activity中。

这样,当Activity执行onDestroy()时,会对其内部的Fragment分发destroy事件,从而通知到Glide创建的空Fragment。

RequestManagerRetriever.java

private RequestManager fragmentGet(@NonNull Context context,
    @NonNull android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
    // 得到传入Activity对应的空Fragment
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    // TODO(b/27524013): Factor out this Glide.get() call.
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

private RequestManagerFragment getRequestManagerFragment(
    @NonNull final android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
    // TAG="com.bumptech.glide.manager"
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment(); // 创建空Fragment
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart(); // 如果宿主Activity可见,则触发lifecycle回调的onStart()
      }
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

ActivityFragmentLifecycle是用于监听Activity、Fragment生命周期的对象,它的实现很简单,有三个方法:

  • onStart():恢复加载任务,在onStart时触发
  • onStop():暂停加载任务,在onStop时触发,注意这里暂停后是可以恢复的
  • onDestroy():终止任务并清空状态,无法再恢复

我认为这里也可以简单点直接使用registerActivityLifecycleCallback来处理

小结

以上完成了对Glide.with()过程的分析,小结一下,共包含以下几个主要步骤:

  1. 判断传入的Context非空、未被Destory
  2. 创建Glide单例(首次)或者获得已创建的单例(非首次)
  3. (首次)初始化Glide单例,通过反射(>=4.0)或者manifest文件(<4.0)加载自定义的AppGlideModule,读取其Options
  4. 如果过传入的context是Application Context,或者在子线程调用,则得到一个绑定了Application生命周期的RequestManager
  5. 如果传入的是Activity、Fragment,且在主线程调用,则得到绑定了Activity生命周期的RequestManager
  6. 内部通过创建一个空Fragment来实现生命周期绑定功能

load()

多种图片来源

with()得到RequestManager后,对其调用load()函数,load()支持加载多种类型的图片来源(如下图),下文取最典型的url场景进行分析。

image.png

load(url)会声明以Drawable形式进行加载,asDrawable()创建一个RequestBuilder,从名字上可以看出使用的是构造器模式。

RequestManager

RequestManager.java

public RequestBuilder<Drawable> load(@Nullable String string) {
  return asDrawable().load(string);
}

public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

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

RequestBuilder

负责设置单次请求的Option并且构建出请求对象,它的构造函数如下,会保存传入的glide、requestManager、transcodeClass(目标解码类,这里是Drawable)、context,并且应用RequestManager的设置。

protected RequestBuilder(
    @NonNull Glide glide,
    RequestManager requestManager,
    Class<TranscodeType> transcodeClass,
    Context context) {
  this.glide = glide;
  this.requestManager = requestManager;
  this.transcodeClass = transcodeClass;
  this.context = context;
  this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
  this.glideContext = glide.getGlideContext();
  // 将RequestManager中定义的listener、option设置进RequestBuilder
  initRequestListeners(requestManager.getDefaultRequestListeners());
  apply(requestManager.getDefaultRequestOptions());
}

创建完RequestBuilder对象后,对其调用load(Object),它会调用自身的loadGeneric()函数,将入参(url)设置给model成员变量,并返回自身。model可以理解为你要加载的这个东西,无论是url、Bitmap还是Drawable都可以作为model。

注意,对于传入String类型url的情况,会把url作为缓存Key使用。

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

小结

以上就是load()函数的过程,对于加载图片url的场景,会以Drawable形式进行加载,构建出RequestBuilder对象后,设置RequestManager中定义的optionslistener,随后返回RequestBuilder对象。

into()

上一步得到初始化RequestBuilder后,对其调用into(ImageView)以完成图片加载,这是Glide加载过程中最复杂的部分,我将详细对此进行分析。首先看into()函数本身。

三项基本职责

into()的目的是向ImageView中加载图片,根据目标ImageView的状态,它有3个职责。

  1. 对构建好的RequestBuilder,执行加载流程
  2. 如果目标ImageView已经有了加载任务正在执行中,取消之
  3. 释放目标ImageView中曾经加载过的资源

ReqeustBuilder.java

@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
  Util.assertMainThread(); // 断言处于主线程,只能在主线程进行into调用
  Preconditions.checkNotNull(view);

  BaseRequestOptions<?> requestOptions = this;
  // 设置Transformation,如圆角、居中裁切等,类似ScaleType
  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()) { // 使用xml中声明的scaleType
      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(
      glideContext.buildImageViewTarget(view, transcodeClass), // 构建Target
      /*targetListener=*/ null,
      requestOptions,
      Executors.mainThreadExecutor());
}

into()函数首先进行主线程判断,之所以这么做的原因是,在后续完成图片资源加载后需要设置给ImageView,Android系统要求只能在主线程操作View。随后设置Transformations变换,然后调用同名into()函数。

在调用同名into()函数时,使用的第一个参数是glideContext.buildImageViewTarget(view, transcodeClass),这里transcodeClassDrawable.class,这一行代码的含义是构建将Drawable显示在ImageView中的Target对象,该Target对象负责将加载完成的Drawable与ImageView关联(其实就是显示在ImageView中)通过这种方式隐藏了Target和transcodeClass的具体实现,达到解耦的目的,可以将Bitmap或者Drawable显示在ImageView里面。

继续看另一个into()函数。

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  Preconditions.checkNotNull(target);
  if (!isModelSet) { // model是我们图片的url地址
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }

  // Step 1 组装Request
  Request request = buildRequest(target, targetListener, options, callbackExecutor);

  Request previous = target.getRequest();
  if (request.isEquivalentTo(previous) // 与上一个请求相同,次要逻辑,略过
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    request.recycle();
    // If the request is completed, beginning again will ensure the result is re-delivered,
    // triggering RequestListeners and Targets. If the request is failed, beginning again will
    // restart the request, giving it another chance to complete. If the request is already
    // running, we can let it continue running without interruption.
    if (!Preconditions.checkNotNull(previous).isRunning()) {
      // Use the previous request rather than the new one to allow for optimizations like skipping
      // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
      // that are done in the individual Request.
      previous.begin();
    }
    return target;
  }

  requestManager.clear(target); // Step 2 清空target当前任务
  target.setRequest(request); // Step 3 将Request绑定至target
  requestManager.track(target, request); // Step 4 执行request

  return target;
}

into()的4个步骤

主要有4个步骤,我们逐步分析。

Step.1 组装Request

调用buildRequest()最终会调用到buildRequestRecursive(),它会组装2个请求,分别是主图请求(mainRequest)和error占位图请求(errorRequest,构建请求时设置error()触发)。

private Request buildRequestRecursive(
    Target<TranscodeType> target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @Nullable RequestCoordinator parentCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    BaseRequestOptions<?> requestOptions,
    Executor callbackExecutor) {

  // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
  ErrorRequestCoordinator errorRequestCoordinator = null;
  if (errorBuilder != null) {
    errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
    parentCoordinator = errorRequestCoordinator;
  }

  Request mainRequest =
      buildThumbnailRequestRecursive( // Request 1 主图片请求,包含缩略图
          target,
          targetListener,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          requestOptions,
          callbackExecutor);

  if (errorRequestCoordinator == null) {
    return mainRequest;
  }

  int errorOverrideWidth = errorBuilder.getOverrideWidth();
  int errorOverrideHeight = errorBuilder.getOverrideHeight();
  if (Util.isValidDimensions(overrideWidth, overrideHeight)
      && !errorBuilder.isValidOverride()) {
    errorOverrideWidth = requestOptions.getOverrideWidth();
    errorOverrideHeight = requestOptions.getOverrideHeight();
  }

  Request errorRequest =
      errorBuilder.buildRequestRecursive( // Request 2(可选)error占位图
          target,
          targetListener,
          errorRequestCoordinator,
          errorBuilder.transitionOptions,
          errorBuilder.getPriority(),
          errorOverrideWidth,
          errorOverrideHeight,
          errorBuilder,
          callbackExecutor);
  errorRequestCoordinator.setRequests(mainRequest, errorRequest);
  return errorRequestCoordinator;
}
buildThumbnailRequestRecursive & obtainRequest

构建主图请求的函数名叫buildThumbnailRequestRecursive(),这里其实容易引起误解,明明是主图的请求,为什么叫做“缩略图”请求呢?这是因为在设置RequestBuilder时,可以通过thumbnail()函数设置size更小的占位图,当占位图请求先返回时,会优先展示占位图,随后替换为更大的主图展示,从而减少用户等待时间。逻辑最终会落入obtainRequest()函数。

obtainRequest()

RequestBuilder.java

private Request obtainRequest(
    Target<TranscodeType> target,
    RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> requestOptions,
    RequestCoordinator requestCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    Executor callbackExecutor) {
  return SingleRequest.obtain(
      context,
      glideContext,
      model,
      transcodeClass,
      requestOptions,
      overrideWidth,
      overrideHeight,
      priority,
      target,
      targetListener,
      requestListeners,
      requestCoordinator,
      glideContext.getEngine(),
      transitionOptions.getTransitionFactory(),
      callbackExecutor);
}

SingleRequest.obtain(),从名字可以看出这是一个运用了对象池的生成器,类似于Android中Message.obtain()。果不其然,取出/新建Request后调用init(),内容也不复杂,无非是设置一大堆变量,需要注意最后一行,将status设置成PENDING状态。

SingleRequest.java

public static <R> SingleRequest<R> obtain(
    Context context,
    GlideContext glideContext,
    Object model,
    Class<R> transcodeClass,
    BaseRequestOptions<?> requestOptions,
    int overrideWidth,
    int overrideHeight,
    Priority priority,
    Target<R> target,
    RequestListener<R> targetListener,
    @Nullable List<RequestListener<R>> requestListeners,
    RequestCoordinator requestCoordinator,
    Engine engine,
    TransitionFactory<? super R> animationFactory,
    Executor callbackExecutor) {
  @SuppressWarnings("unchecked") SingleRequest<R> request =
      (SingleRequest<R>) POOL.acquire();
  if (request == null) {
    request = new SingleRequest<>();
  }
  request.init(
      context,
      glideContext,
      model,
      transcodeClass,
      requestOptions,
      overrideWidth,
      overrideHeight,
      priority,
      target,
      targetListener,
      requestListeners,
      requestCoordinator,
      engine,
      animationFactory,
      callbackExecutor);
  return request;
}

private synchronized void init(
    Context context,
    GlideContext glideContext,
    Object model,
    Class<R> transcodeClass,
    BaseRequestOptions<?> requestOptions,
    int overrideWidth,
    int overrideHeight,
    Priority priority,
    Target<R> target,
    RequestListener<R> targetListener,
    @Nullable List<RequestListener<R>> requestListeners,
    RequestCoordinator requestCoordinator,
    Engine engine,
    TransitionFactory<? super R> animationFactory,
    Executor callbackExecutor) {
  this.context = context;
  this.glideContext = glideContext;
  this.model = model;
  this.transcodeClass = transcodeClass;
  this.requestOptions = requestOptions;
  this.overrideWidth = overrideWidth;
  this.overrideHeight = overrideHeight;
  this.priority = priority;
  this.target = target;
  this.targetListener = targetListener;
  this.requestListeners = requestListeners;
  this.requestCoordinator = requestCoordinator;
  this.engine = engine;
  this.animationFactory = animationFactory;
  this.callbackExecutor = callbackExecutor;
  status = Status.PENDING; // <--重点:状态为pending

  if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
    requestOrigin = new RuntimeException("Glide request origin trace");
  }
}

Step.2 清空target当前任务

取消当前ImageView上正在Pending的任务,同时将ImageView上已经显示的资源(如Bitmap)释放掉,这里不继续深究了,只需要知道untrack(Target)会清理掉目标Target所关联的请求即可。

public synchronized void clear(@Nullable final Target<?> target) {
  if (target == null) {
    return;
  }

  untrackOrDelegate(target);
}

private void untrackOrDelegate(@NonNull Target<?> target) {
  boolean isOwnedByUs = untrack(target);
  if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
    Request request = target.getRequest();
    target.setRequest(null);
    request.clear();
  }
}

Step.3 将Request绑定至target

target.setRequest(request),对于ImageView,会将Reqeust对象设置为它的Tag,如果有设置多个Tag的需求还需要传入tagId参数,默认是没有tagId的。

Step.4 执行Request

到这里,已经把ImageView与Request通过Tag进行关联,接下来就是执行请求的过程。

RequestManager.java

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  targetTracker.track(target); // 将target放入Set集合中
  requestTracker.runRequest(request); // 真正发起请求
}
runRequest()

boolean类型的isPaused变量默认是false,从而调用request.begin()

RequestTracker.java

public void runRequest(@NonNull Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    request.clear();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Paused, delaying request");
    }
    pendingRequests.add(request);
  }
}
onSizeReady()

begin()是一个接口方法,其实现位于SingleRequest.java,抛去无关主流程的判断,它会调用onSizeReady()函数,启动Engine进行加载。

SingleRequest.java

@Override
public synchronized void begin() {
  assertNotCallingCallbacks(); // 阻止在error时手动发起Request
  stateVerifier.throwIfRecycled();
  startTime = LogTime.getLogTime();
  ...
  // 准备开始请求
  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()); // 通知开始加载,无特殊逻辑,不需要关心
  }
  ...
}

Engine对象是在Glide构造函数里就已经初始化的,在调用engine.load()时,传入的倒数第二个参数是ResourceCallback类型的回调,也就是SingleRequest对象自身,它实现了onResourceReady()onLoadFailed()两个方法。

SingleRequest.java

@Override
public synchronized void onSizeReady(int width, int height) {
  ...
  if (status != Status.WAITING_FOR_SIZE) { // 必须在status=WAITING_FOR_SIZE时才能进入
    return;
  }
  status = Status.RUNNING; // 切换status为RUNNING
   
  // 是否需要放大显示
  float sizeMultiplier = requestOptions.getSizeMultiplier();
  this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
  this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

  if (IS_VERBOSE_LOGGABLE) {
    logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
  }
  loadStatus =
      engine.load(
          glideContext,
          model,
          requestOptions.getSignature(),
          this.width,
          this.height,
          requestOptions.getResourceClass(),
          transcodeClass,
          priority,
          requestOptions.getDiskCacheStrategy(),
          requestOptions.getTransformations(),
          requestOptions.isTransformationRequired(),
          requestOptions.isScaleOnlyOrNoTransform(),
          requestOptions.getOptions(),
          requestOptions.isMemoryCacheable(),
          requestOptions.getUseUnlimitedSourceGeneratorsPool(),
          requestOptions.getUseAnimationPool(),
          requestOptions.getOnlyRetrieveFromCache(),
          this,
          callbackExecutor);

  ...
}
Engine.load()执行流程

engine.load()函数的执行流程如下:

  1. 根据传入的参数,生成唯一Key,该Key用于判断请求是否雷同
  2. 先检索ActiveResources活动缓存,这是当前正在其它View中显示的图像,如果发现是相同的请求,则直接获取其加载好的资源。这里用到的是内存缓存Memory Cache,但没有容量上限(因为正在其它ImageView显示中)
  3. 然后检索真正的Memory Cache,它是在Glide对象初始化时设置的,大小为屏幕长*宽*4(字节)*2(屏幕数),类型是LruCache,实现得很精妙
  4. 如果Memory Cache命中失败,就看当前正在执行的job里面,有没有与key相同的job。若找到了就把新的callback赋给旧的job并返回,若没找到则继续执行
  5. 创建engineJob,它用于封装加载结果的回调,如加载成功/失败。接着创建了decodeJob,并把engineJob传入给decodeJob
  6. 调用engineJob.start(decodeJob),启动任务

Engine.java

public synchronized <R> LoadStatus load(
    GlideContext glideContext,
    Object model,
    Key signature,
    int width,
    int height,
    Class<?> resourceClass,
    Class<R> transcodeClass,
    Priority priority,
    DiskCacheStrategy diskCacheStrategy,
    Map<Class<?>, Transformation<?>> transformations,
    boolean isTransformationRequired,
    boolean isScaleOnlyOrNoTransform,
    Options options,
    boolean isMemoryCacheable,
    boolean useUnlimitedSourceExecutorPool,
    boolean useAnimationPool,
    boolean onlyRetrieveFromCache,
    ResourceCallback cb,
    Executor callbackExecutor) {
  long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

  // 生成key用于集合及比对
  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
      resourceClass, transcodeClass, options);

  EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
  if (active != null) {
    cb.onResourceReady(active, DataSource.MEMORY_CACHE);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from active resources", startTime, key);
    }
    return null;
  }

  EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
  if (cached != null) {
    cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return null;
  }

  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
  if (current != null) {
    current.addCallback(cb, callbackExecutor);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Added to existing load", startTime, key);
    }
    return new LoadStatus(cb, current);
  }

  EngineJob<R> engineJob =
      engineJobFactory.build(
          key,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache);

  DecodeJob<R> decodeJob =
      decodeJobFactory.build(
          glideContext,
          model,
          key,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          onlyRetrieveFromCache,
          options,
          engineJob);

  jobs.put(key, engineJob);

  engineJob.addCallback(cb, callbackExecutor);
  engineJob.start(decodeJob);

  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime, key);
  }
  return new LoadStatus(cb, engineJob);
}
Memory Cache 的 LRU 实现

这个类实现得非常之优雅,按照字节容量大小来判断是否需要移除最少使用的,而不是简单的按元素个数判断。

public class LruCache<T, Y> {
  private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
  private final long initialMaxSize;
  private long maxSize;
  private long currentSize;
  
  public LruCache(long size) {
    this.initialMaxSize = size;
    this.maxSize = size;
  }

  // 元素的“大小”,对于图片类型可以重载,以返回其字节数
  protected int getSize(@Nullable Y item) {
    return 1;
  }
  
  protected synchronized int getCount() {
    return cache.size();
  }
  
  // LinkedHashMap是队列,先进先出
  @Nullable
  public synchronized Y put(@NonNull T key, @Nullable Y item) {
    final int itemSize = getSize(item);
    if (itemSize >= maxSize) {
      onItemEvicted(key, item); // 如果新item直接超出容量则移它
      return null;
    }

    if (item != null) {
      currentSize += itemSize;
    }
    @Nullable final Y old = cache.put(key, item);
    if (old != null) {
      currentSize -= getSize(old); // key相同,替换掉old,需要减掉它的size

      if (!old.equals(item)) {
        onItemEvicted(key, old); // 触发移除回调
      }
    }
    evict(); // 判断size

    return old;
  }

  // 不断移除最久使用的元素,以便size不超过max
  protected synchronized void trimToSize(long size) {
    Map.Entry<T, Y> last;
    Iterator<Map.Entry<T, Y>> cacheIterator;
    while (currentSize > size) {
      cacheIterator  = cache.entrySet().iterator(); // 因为是有序队列,所以直接取第一个就是最早插入的
      last = cacheIterator.next();
      final Y toRemove = last.getValue();
      currentSize -= getSize(toRemove);
      final T key = last.getKey();
      cacheIterator.remove();
      onItemEvicted(key, toRemove);
    }
  }

  private void evict() {
    trimToSize(maxSize);
  }
}
EngineJob.start()

先判断是否使用磁盘缓存,默认是使用的。Executor含义是执行器,它会用来执行decodeJob,其实就是调用Job.run()函数,因为Job本身是一个Runnable对象。

public synchronized void start(DecodeJob<R> decodeJob) {
  this.decodeJob = decodeJob;
  GlideExecutor executor = decodeJob.willDecodeFromCache()
      ? diskCacheExecutor
      : getActiveSourceExecutor();
  executor.execute(decodeJob);
}
磁盘缓存DiskCache

diskCacheExecutor是在GlideBuilder中进行初始化的,其实现位于GlideExecutor,其实不仅是diskCacheExecutor,另外3个SourceExecutor, UnlimitedSourceExecutorAnimationExecutor同样是在这里进行初始化。

它们在线程数、保活时间上有所区别。

GlideExecutor.java

public static GlideExecutor newDiskCacheExecutor() {
  return newDiskCacheExecutor(
      DEFAULT_DISK_CACHE_EXECUTOR_THREADS, // 1条线程
      DEFAULT_DISK_CACHE_EXECUTOR_NAME,
      UncaughtThrowableStrategy.DEFAULT);
}

public static GlideExecutor newDiskCacheExecutor(
    int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
  return new GlideExecutor(
      new ThreadPoolExecutor(
          threadCount /* corePoolSize */,
          threadCount /* maximumPoolSize */,
          0 /* keepAliveTime */, // 不保活,用完即扔
          TimeUnit.MILLISECONDS,
          new PriorityBlockingQueue<Runnable>(),
          new DefaultThreadFactory(name, uncaughtThrowableStrategy, true)));
}

通过executor.execute()方法,调用到DecodeJob.run()

decodeJob.run()

run()内部调用runWrapped(),首次进入时,runReason=INITIALIZEgetNextState()返回State.RESOURCE_CACHE

private void runWrapped() {
  switch (runReason) {
    case INITIALIZE: // 首次进入时
      stage = getNextStage(Stage.INITIALIZE);
      currentGenerator = getNextGenerator();
      runGenerators();
      break;
    case SWITCH_TO_SOURCE_SERVICE:
      runGenerators();
      break;
    case DECODE_DATA:
      decodeFromRetrievedData();
      break;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}

private Stage getNextStage(Stage current) {
  switch (current) {
    case INITIALIZE:
      return diskCacheStrategy.decodeCachedResource() // 默认true
          ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
    case RESOURCE_CACHE:
      return diskCacheStrategy.decodeCachedData()
          ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
    case DATA_CACHE:
      // Skip loading from source if the user opted to only retrieve the resource from cache.
      return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
    case SOURCE:
    case FINISHED:
      return Stage.FINISHED;
    default:
      throw new IllegalArgumentException("Unrecognized stage: " + current);
  }
}
数据来源的分类

Stage是枚举类,表明当前要decode的数据来源:

  • RESOURCE_CACHE:在将图片保存为显示大小的磁盘缓存查找
  • DATA_CACHE:在将图片保存为原始大小的磁盘缓存查找
  • SOURCE:从数据真实来源(如网络)读取

DecodeJob.java

/**
 * Where we're trying to decode data from.
 */
private enum Stage {
  /** The initial stage. */
  INITIALIZE,
  /** Decode from a cached resource. */
  RESOURCE_CACHE,
  /** Decode from cached source data. */
  DATA_CACHE,
  /** Decode from retrieved source. */
  SOURCE,
  /** Encoding transformed resources after a successful load. */
  ENCODE,
  /** No more viable stages. */
  FINISHED,
}
Generator、Loader与Fetcher

对于不同的stagegetNextGenerator()返回相应的Generator进行处理。其中ResourceCacheDataCache是从磁盘缓存查找,如果没有命中,则调用SourceGenerator从数据源头获取。

private DataFetcherGenerator getNextGenerator() {
  switch (stage) {
    case RESOURCE_CACHE:
      return new ResourceCacheGenerator(decodeHelper, this);
    case DATA_CACHE:
      return new DataCacheGenerator(decodeHelper, this);
    case SOURCE:
      return new SourceGenerator(decodeHelper, this);
    case FINISHED:
      return null;
    default:
      throw new IllegalStateException("Unrecognized stage: " + stage);
  }
}

每一个Generator都实现了startNext()函数,在Glide初始化时会注册一系列Loader,其中就有获取网络图片的HttpGlideUrlLoader,它使用HttpUrlFetcher获取数据。

SourceGenerator.java

@Override
public boolean startNext() {
  if (dataToCache != null) { // <--把上次成功获取到的数据进行缓存
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }

  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
    return true;
  }
  sourceCacheGenerator = null;

  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    loadData = helper.getLoadData().get(loadDataListIndex++);
    if (loadData != null
        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
        || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
      started = true;
      loadData.fetcher.loadData(helper.getPriority(), this); // <--从网络加载图片InputStream
    }
  }
  return started;
}

HttpUrlFetcher从网络加载图片字节流。

HttpUrlFetcher.java

@Override
public void loadData(@NonNull Priority priority,
    @NonNull DataCallback<? super InputStream> callback) {
  long startTime = LogTime.getLogTime();
  try {
    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    callback.onDataReady(result);
  } catch (IOException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Failed to load data for url", e);
    }
    callback.onLoadFailed(e);
  } finally {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

Generator包装了LoaderLoader包装了Fetcher

处理顺序总结如下,其中磁盘缓存可以在Options中设置以跳过。

  1. INITIALIZE:初始化状态,直接向下执行
  2. RESOURCE_CACHE:位于磁盘的,转换后图片缓存,大小为View大小。转换Transformation如剪裁、应用过滤器等
  3. DATA_CACHE:位于磁盘的,原始图片缓存
  4. SOURCE:从数据来源获取,如服务器
  5. FINISHED
decodeFromRetrievedData()

获取到资源后,解码成Bitmap或者Gif,最后通过回调交给ImageView进行展示。

参考资料