Glide源码解析(三)

166 阅读11分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

Glide源码解析(一)

Glide源码解析(二)

基本用法

Glide最基本的用法如下:

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

本文就从这三个方法去学习Glide的源码。

PS:本文基于4.13.2版本的Glide源码。

废话不多说直接开始!

into

into方法是Glide实际开始工作的地方,非常复杂。我们先来看看into的代码:

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
​
    //省略一些代码
​
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

可以看到,入参是一个ImageView,返回值是一个ViewTarget。但是就基本用法而言,调用者并不会关心返回的这个ViewTarget。ViewTarget的两个泛型分别表示需要加载的目标View(必须是View的子类,这里即是ImageView)以及第二篇说到的ResourceType。

继续跟踪这个into的重载方法。

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 = buildRequest(target, targetListener, options, callbackExecutor);
​
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
​
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);
​
    return target;
  }

先看返回类型Y。Y是Target的子类,由一开始那个into方法可以判断出,Y应该是ViewTarget<ImageView,Drawable>,而ViewTarget确实是Target的子类。也就说在我们这个例子中,Y实际上是Target。

接着来看入参:

  • Y类型的target:从上面的分析可以判断出,target的类型是Target。这个target是通过一开始那个into中生成并传入的,我们回头看看这个生成的方法:
// #ClideContext
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }

这个buildImageViewTarget是GlideContext的方法,而GlideContext则是在Glide生成实例的时候生成的,里面包含了所有加载资源所需的注册表和类(Engine、默认参数。一些工厂类等)。

通过上面的分析我们可以推测出这个X泛型的实际类型为Drawable。再看看ImageViewTargetFactory如何生产ViewTarget。

public class ImageViewTargetFactory {
  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)");
    }
  }
}

同理这个Z泛型也是Drawable(这Glide真的多泛型……)。代码很简单,判断Z是Bitmap还是Drawable,分别返回BitmapImageViewTarget和DrawableImageViewTarget。

由于我们这里的例子是Drawable,那自然生产出来的是DrawableImageViewTarget。(这里要关注一下Drawable.class.isAssignableFrom(clazz)这个判断条件,只要clazz是Drawable的子类就返回true)

说完了target,继续说into的其他入参!

  • targetListeners:传空不管了。
  • options:配置Request的options。
  • callbackExecutor:回调的Executor,这里传入了主线程Executor,也就是回调将会在主线程调用。

看完了返回和入参,我们继续阅读一下into的代码,这里重贴一遍:

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 = buildRequest(target, targetListener, options, callbackExecutor);
​
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
​
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);
​
    return target;
  }

一行行看!

  • 检查target是否非空。
  • 未调用load不允许调用into,都不知道去哪获取那肯定是不能执行获取啦!
  • 构建Request。
  • 判断构建的request是不是与当前的request(即代码中的previous,实际上这时候构建出来的request并没有设到target中,所以previous其实是“当前的”request)相同,并且当前request还没完成并打开了内存缓存,并且当前request没有在运行,则直接调用当前request.begin。也就是说在打开了内存缓存的情况下,执行相同的request会忽略掉新执行的request。这是request的重用优化策略。
  • 假如确实是一个新的request,则清除当前的request。
  • 将新request设入target。
  • requestManager跟踪target以及新request。
  • 返回target。

乍一看,request也没有被执行啊?点开了唯一有可能执行request的地方——requestManager.track。

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

果不其然,runRequest藏在了这里!吐槽一下,track就是track,run就是run,把run藏在track里实属让人头大。

到了这里,我觉得应该要解释一下Target与Request之间的关系。

  • Target:Glide加载图片后送达的目的地,可以理解为结果回调。并且继承了Glide生命周期的监听接口(LifecycleListener),这也意味着Target拥有能感知生命周期的能力。
  • Request:负责请求的类。

至于两者的关系,可谓是有点错综复杂。首先一句话总结:两者在一定程度上互相持有。为什么说是一定程度上呢?因为在ViewTarget(即需要加载图片的是一个View时)的子类里,Target对Request的持有是通过View.setTag的方式间接持有的;而在非ViewTarget的Target中,则是以成员变量的方式直接持有的。Request则是直接以成员变量的方式持有Target的。其实在Request接口的定义里面,是找不到和Target有关联的蛛丝马迹的,但是所有的Request实际上执行请求的都是SingleRequest这个实现类,而在这个实现类的成员变量里,就有着Target,持有Target的目的是为了能回调请求的生命周期。(这里其实我有点疑惑,既然所有的请求都得回调生命周期给Target,为什么不在Request接口定义的时候就把Target相关的东西也定义好呢?这样不会减少错误使用的几率吗?)

classDiagram
class Target{
	+setRequest(Request)
	+getRequest()
}
class BaseTarget
class ViewTarget{
	+View view
}
class ImageViewTarget
class DrawableImageViewTarget
class BitmapImageViewTarget

class Request
class SingleRequest

<<interface>> Target
<<interface>> Request
<<abstract>> BaseTarget
<<abstract>> ViewTarget
<<abstract>> ImageViewTarget

Target <|.. BaseTarget
BaseTarget <|.. ViewTarget
ViewTarget <|-- ImageViewTarget
ImageViewTarget <|-- DrawableImageViewTarget
ImageViewTarget <|-- BitmapImageViewTarget

Request o--o Target
Request <|.. SingleRequest

说完Target与Request之间的关系,再来说说TargetTracker以及RequestTracker吧!

  • TargetTracker:RequestManager中,用来管理当前活跃的Target集合的类。实现了Glide生命周期监听接口(LifecycleListener)并持有当前活跃的全部Target(以Set储存)。当Glide生命周期事件被触发时,TargetTracker负责遍历所有活跃的Target并传递相关的生命周期事件。
  • RequestTracker:负责管理(追踪、取消、重试)各种状态(正在执行、已完成、失败)的Request。Glide具有生命周期感知能力并能自行暂停、恢复以及取消任务的能力,正是在RequestTracker中实现的。RequestTracker中维护了两个集合:requests——所有Request集合;pendingRequests——处于暂停状态的Request集合。当需要暂停请求时,将requests集合中正在执行中的Request暂停并放入pendingRequests集合中;恢复请求时,则将pendingRequests集合中的Request重新执行,然后清空pendingRequests集合。值得注意的是,requests集合是一个弱引用集合。弱引用肯定是为了避免内存泄漏的,但是为什么会出现内存泄漏我想不通,如果有兄弟姐妹知道希望不吝赐教。

让我们再次把目光移回到RequestTracker.runRequest方法中。

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

做的事情也很简单,把传入的request放入requests集合中,并调用request的begin方法(只讨论非暂停状态)。

由于Request是个接口,如果要看实际的操作,那就要找到Request的实现类。虽然上面已经剧透了实际上的请求都是SingleRequest这个实现类,但还是探究一下这个SingleRequest是如何构建出来的。让我们回到into方法里面的构建Request的代码中,也就是buildRequest方法,该方法简单地调用了buildRequestRecursive。

private Request buildRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    //①
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
​
    //②
    Request mainRequest =
        buildThumbnailRequestRecursive(
            requestLock,
            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(
            requestLock,
            target,
            targetListener,
            errorRequestCoordinator,
            errorBuilder.transitionOptions,
            errorBuilder.getPriority(),
            errorOverrideWidth,
            errorOverrideHeight,
            errorBuilder,
            callbackExecutor);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

老规矩先看入参:

  • requestLock:用于加锁的对象,传入了一个new Object()。
  • target:本例中就是DrawableImageViewTarget。
  • targetListener:一开始传入为null,则这里也为null。
  • parentCoordinator:传入了null。
  • transitionOptions:对资源进行处理的配置项。
  • priority:请求的优先级。
  • overrideWidth:将资源resize的宽度。
  • overrideHeight:将资源resize的高度。
  • requestOptions:自定义配置项。
  • callbackExecutor:回调的Executor,本例中为主线程。

接下来看代码,主要分成三个部分:

  • ①处代码。如果errorBuilder不为空,初始化一个ErrorRequestCoordinator。这里浅浅解释一下,其实在本例的基本用法中,是不会出现这个ErrorRequestCoordinator的。ErrorRequestCoordinator本质上也是一个Request,里面持有两个Request:一个正常的Request,一个错误Request。当正常Request出现加载失败时候,就会自动执行ErrorRequest,加载指定的错误资源。但没调用过RequestBuilder.error的话是没有这个errorBuilder的。
  • ②处代码。构建一个mainRequest并返回该Request。(不考虑设置了errorBuilder的情况)
  • ③处代码。这里就是设置了errorBuilder的情况下,会将mainRequest作为正常Request传入ErrorRequestCoordinator中,返回ErrorRequestCoordinator。

自然②处代码是关键地方,继续看看构建mainRequest:

private Request buildThumbnailRequestRecursive(
    //入参忽略  
    ) {
    if (thumbnailBuilder != null) {
      //设置了thumbnailBuilder,会生成一个ThumbnailRequestCoordinator,参考ErrorRequestCoordinator,本例省略。
    } else if (thumbSizeMultiplier != null) {
      //设置了thumbSizeMultiplier,同样是生成ThumbnailRequestCoordinator。省略。
    } else {
      //没设置thumbnail,本例情况。
      return obtainRequest(
          requestLock,
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          callbackExecutor);
    }
  }

由于本例没有设置thumbnail(缩略图),所以直接就是走到obtainRequest,obtainRequest调用了SingleRequest.obtain,进而调用SingleRequest的构造方法,没啥知识点,这里就不看了,知道获取到了SingleRequest就ok了。

还记得RequestTracker的runRequest方法吗?调用了Request.begin。而通过obtainRequest获取到的Request实际上是SingleRequest,那我们就直接看SingleRequest的begin方法吧!

public void begin() {
    synchronized (requestLock) {
      //①
      if (model == null) {
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
          width = overrideWidth;
          height = overrideHeight;
        }
          
        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
        onLoadFailed(new GlideException("Received null model"), logLevel);
        return;
      }
​
      //②
      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }
​
      //③
      if (status == Status.COMPLETE) {
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, false);
        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());
      }
    }
  }

PS:上述代码经过删减。

在阅读这部分代码之前,首先看一下SingleRequest的状态枚举类Status的状态流转。

stateDiagram
[*] --> PENDING : 初始化
PENDING --> WAITING_FOR_SIZE : begin()
WAITING_FOR_SIZE --> RUNNING : onSizeReady()
RUNNING --> COMPLETE : onResourceReady()
RUNNING --> FAILED : onLoadFailed()
PENDING --> FAILED : model == null
COMPLETE --> [*]
FAILED --> [*] 

图很简单,就不多做解释了。(PS:实际上的Status还有一个CLEARED,但是为了保持图的简洁明了就删掉了,问题也不大,我们只关注主要流程)

现在开始逐个部分来看begin的代码:

  • ①处代码。如果model为空直接调用onLoadFailed。model在本例中则为url。
  • ②处代码。如果该Request正在执行则抛出异常。
  • ③处代码。将Status设为WAITING_FOR_SIZE。假如设定了overrideSize则直接使用overrideSize并调用onSizeReady;反之调用Target的getSize异步计算Size,Target计算完成后会调用Request的onSizeReady。
  • ④处代码。如果Status为RUNNING(对应③处代码指定了overrideSize的情况)或WAITING_FOR_SIZE(对应异步Size的情况)并且可以通知状态的情况下,通知Target开始加载资源(onLoadStarted)。

由上图状态图可以看到,WAITING_FOR_SIZE经过onSizeReady后就会转入RUNNING,那就继续看看onSizeReady。

public void onSizeReady(int width, int height) {
    synchronized (requestLock) {
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;
​
      float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
        
      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);
​
      if (status != Status.RUNNING) {
        loadStatus = null;
      }
    }
  }

这里的代码也是非常简单,计算一下宽高,然后调用engine.load方法。

说到这个Engine,是整个Glide中承担请求、解码、缓存、变换的核心部分。它在Glide中也是以单例的形式存在,它伴随着Glide单例初始化而初始化。

接下来看看Engine.load。

public <R> LoadStatus load(
      //由于入参太多,这里删除一下。
      ) {
    //①
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);
​
    EngineResource<?> memoryResource;
    synchronized (this) {
      //②
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
​
      //③
      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }
​
    //④
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE, false);
    return null;
  }
  • ①处代码。根据传入的各种参数生成一EngineKey,这个EngineKey是用于判断Request是不是相同,并且是缓存该Request资源的凭证(由于储存资源的容器是Map,所以该凭证也即是Map的Key)。
  • ②处代码。从内存缓存中根据生成的Key寻找对应的Resource。
  • ③处代码。如果不存在于内存缓存中,则试图从正在执行的任务中获取Resource或开始一个新的获取任务(即无对应的正在执行的任务)并返回该任务的LoadState。
  • ④处代码。如果存在内存缓存,则直接调用onResourceReady返回Resource并指定该结果为内存缓存。返回一个空的LoadState。

本例假设没有存在内存缓存,即走到③处代码中。接下来看看waitForExistingOrStartNewJob。

private <R> LoadStatus waitForExistingOrStartNewJob(
      //入参太多,删除掉
      ) {
​
    //①
    EngineJob<?> current = jobs.get(key, onlyRetrieeFromCache);
    //②
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      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);
​
    return new LoadStatus(cb, engineJob);
  }

这段代码跟上一段的整体逻辑也是差不多。

  • ①处代码。根据EngineKey从jobs(Jobs持有以EngineKey为键,EngineJob为值的Map,缓存着正在进行的Job)检索对应的Job。这里可以看出,无论是Resource还是EngineJob,都是以EngineKey作为唯一凭证的。
  • ②处代码。如果从jobs中检索到对应的Job,则直接将cb(ResourceCallback,本例中即为SingleRequest)设入检索到的job中,并返回LoadState。因为LoadState只涉及取消请求,所以这里不展开了。
  • ③处代码。没有检索到对应的Job,则生成新的Job。这里又两个Job:EngineJob以及DecodeJob。DecodeJob负责将EngineJob请求回来的数据进行解码,也就是DecodeJob是EngineJob的一个组件。创建好了EngineJob,将其放入jobs中,设置好cd后调用EngineJob.start启动任务,最后返回LoadState。

至此,终于开始要真正请求和处理图片了,由于篇幅问题,后面的解析会再开一篇。本来是想着一个方法出一篇的,可是没想到这个into这么复杂。继续老规矩,整理一个图总结一下。

sequenceDiagram
RequestBuilder ->> RequestBuilder :into()
RequestBuilder ->> ReqestManager:buildRequest()
ReqestManager ->> RequestTracker:track()
RequestTracker ->> SingleRequest:runRequest()
SingleRequest ->> SingleRequest:onSizeReady()
SingleRequest ->> Engine:load()
Engine ->> EngineJob:waitForExistingOrStartNewJob()
EngineJob ->> EngineJob:start()