Glide源码流程分析

1. Glide总览

Glide图片加载框架从总体来看采用三段论来让用户轻松加载图片。

  • with()方法主要通过创建一个空白的Fragment盖在展示的页面上,感知展示页面的生命周期,来动态调整Glide内部请求等业务。详细参见Glide源码分析-生命周期管理
  • load()方法完成通用请求的构建
  • into()方法主要维护这运行队列和等待队列,当生命周期改变的时候,动态清除一些不必要加载的请求。同时还维护着活动缓存和内存缓存,多级缓存有利于节约资源和加载效率,最后如果没有缓存就采用网络请求的方式加载请求的内容。
Glide总览.png

2. with()

with()方法的详细分析可以参见上一篇Glide的叙述:Glide源码分析-生命周期管理

Glide_with.png

总体来讲,with()方法中发生了如下事情:

  • Glide类:主要做一些 init 工作,比如缓存,线程池,复用池的构建等等。
  • RequestManagerRetriever类:主要是获得一个 RequestManager 请求管理类,然后绑定一个 Fragment
  • SupportRequestManagerFragment 类:用于管理请求的生命周期。
  • RequestManager:主要用于对请求的管理封装。

3. load()

load()流程其实不是很复杂,主要做了通用资源类型的设置选项和启动负载,在RequestBuilder 中完成。流程如下图:

Glide_with_load.png

RequestBuilder : 这是一个通用请求构建类,可以处理通用资源类型的设置选项和启动负载。

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
      
  .....
    
  public RequestBuilder<Drawable> load(@Nullable String string) {
    //这里调用 Drawable 图片加载请求器为其加载
    return asDrawable().load(string);
  }
  
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }


  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }
    
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }         
}
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
        
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  // 描述加载的数据源-这里可以看做是我们传递进来的 http://xxxx.png
  @Nullable private Object model;
  // 描述这个请求是否已经添加了加载的数据源
  private boolean isModelSet;
  
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }
  
  // model是我们需要加载的数据源,可能为null,为了保证model一定被赋值,此处引进isModelSet来保证。
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
      this.model = model;
      isModelSet = true;
      return this;
  }
}

到这里 RequestBuilder 就构建好了, RequestBuilder构建出来,接下来就是重头戏into()。

4.into()

into()是所有流程中最最复杂的方法,有60多个步骤,此处我们分成几个环节来一一分析流程。

分析之前,我们脑子中一定记住从最初是状态开始翻代码,最初始状态就是:拿到了url,各级缓存都没有资源,需要从网上下载资源,然后加载到控件中。

into()整体流程图在Glide工程中src目录中

4.1 确定控件大小

我们从网上加载的inputstram不可能是下载多少就加载多少,这样内存可能爆掉,或者效率很低,我们应该按需加载,控件多大,我们就将图片压缩到多大加载。

确定控件大小.png
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    // 根据 ImageView 布局中的 scaleType 来重构 requestOptions
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      //如果在 xml ImageView 节点中 没有设置 scaleType 那么默认在构造函数中进行了初始化为   mScaleType = ScaleType.FIT_CENTER;  
      switch (view.getScaleType()) {
       .....
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          //这里用到了克隆(原型设计模式),选择一个"居中合适"显示的方案
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
       ....
      }
    }
    //调用 into 重载函数,创建一个 ViewTarget
    return into(
        //调用 buildImageViewTarget 构建一个 ImageView 类型的 Target(Bitmap/Drawable)
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

上面代码就两大步:

第一步:先拿到当前 ImageView getScaleType 类型的属性,然后重新 clone 一个进行配置;

第二步:调用 into 重载继续构建;

先来看下 glideContext.buildImageViewTarget 是怎么构建出来 ImageViewTarget 的:

  @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    //调用 工厂模式 根据 transcodeClass 生成出一个对应的 ImageViewTarget
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    //如果目标的编码类型属于 Bitmap 那么就创建一个 Bitmap 类型的 ImageViewTarget
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    ////如果目标的编码类型属于 Drawable 那么就创建一个 Drawable 类型的 ImageViewTarget
    } 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)");
    }
  }
}

上面 生产 Target 的时候注意一下,只要调用了 asBitmap 才会执行生产 BitmapImageViewTarget ,所以这里我们关注 Drawable 类型就行了,我们就先简单看看这个 target 内部怎么实现的,因为最后当拿到请求资源之后,会在target中设置资源到view中。

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

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

  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
}

从上面代码可以知道 DrawableImageViewTarget 继承的是 ImageViewTarget 重写的 setResource 函数,实现了显示 Drawable 图片的逻辑。

回到主流程,into()重载方法中。

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    //这里的 isModelSet 是在 load 的时候赋值为 true 的,所以不会抛异常,确保了信息源一定被设置。
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
        //为这个 http://xxx.png 生成一个 Glide request 请求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
        //相当于拿到上一个请求
    Request previous = target.getRequest();
    //下面的几行说明是否与上一个请求冲突,一般不用管 直接看下面 else 判断
    // 如果两个请求参数和大小相同,就不用再请求了,直接返回。此if中也会判断前一个请求是否在请求中等状态,此处我们不用关心,第一次加载图片不会进入此if语句。
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
    //如果是一个新请求,先清理掉目标请求管理
    requestManager.clear(target);
    //重新为目标设置一个 Glide request 请求
    target.setRequest(request);
    //最后是调用 RequestManager 的 track 来执行目标的 Glide request 请求
    requestManager.track(target, request);

    return target;
  }

以上核心就两个点:

第一点:为 target buildRequest 构建一个 Glide request 请求; 

第二点:将构建出来的 Request 交于 RequestManager 来执行;

我们先简单的来看下怎么构建的 Request:

  private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions,
        callbackExecutor);
  }
  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 来为我们构建的 Request 请求对象,开始只是初始化一些配置属性。

继续回到上面看request的track()方法。

//这里对当前 class 加了一个同步锁避免线程引起的安全性  
 synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    //添加一个目标任务  
    targetTracker.track(target);
    //执行 Glide request
    requestTracker.runRequest(request);
  }
public void runRequest(@NonNull Request request) {
    //添加一个请求
    requests.add(request);
    //是否暂停
    if (!isPaused) {
      //没有暂停,开始调用 Request begin 执行
      request.begin();
    } else {
      //如果调用了 暂停,清理请求
      request.clear();
      pendingRequests.add(request);
    }
}

上面的逻辑是先为 requests 添加一个请求,看看是否是停止状态,如果不是就调用 request.begin();执行。

这里的 Request 是一个接口,通过之前我们讲到 buildRequest 函数可知 Request 的实现类是 SingleRequest 我们就直接看它的 begin 函数.

 @Override
  public synchronized void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    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);
      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());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }
@Override
  public void onSizeReady(int width, int height) {
    ......
      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);

      ......
    }
  }

4.2 从不同缓存中搜寻资源

从不同缓存中获取资源过程.png
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) {

    //拿到缓存或者请求的 key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
        //根据 key 拿到活动缓存中的资源
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    //如果 ActiveResources 活动缓存中有就回调出去
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }

    //尝试从 LruResourceCache 中找寻这个资源 
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      //如果内存缓存 Lru 中资源存在回调出去
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }

    //------------- 走到这里说明活动缓存 跟内存 缓存都没有找到 -----------
    
    //根据 Key 看看缓存中是否正在执行
    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);
        //把当前需要执行的 key 添加进缓存
    jobs.put(key, engineJob);
        //执行任务的回调
    engineJob.addCallback(cb, callbackExecutor);
    //开始执行。
    engineJob.start(decodeJob);

    return new LoadStatus(cb, engineJob);
  }

通过 engine.load 这个函数里面的逻辑,我们可以总结3点:

  1. 先构建请求或者缓存 KEY ;

  2. 根据 KEY 从内存缓存中查找对应的资源数据(ActiveResources(活动缓存,内部是一个 Map 用弱引用持有),LruResourceCache),如果有就回调 对应监听的 onResourceReady 表示数据准备好了。

  3. 从执行缓存中查找对应 key 的任务

    1. 如果找到了,就说明已经正在执行了,不用重复执行。

    2. 没有找到,通过 EngineJob.start 开启一个新的请求任务执行。

为了看主线,时刻记住开始强调的,所有缓存中都没有数据!直接看 EngineJob.start

public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //拿到 Glide 执行的线程池
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    //开始执行
    executor.execute(decodeJob);
  }

通过 DecodeJob 源码得知,它是实现的 Runnable 接口,这里 GlideExecutor 线程池开始执行,就会启动 DecodeJob 的 run 函数。

细心的小伙伴可以看出engineJob之后就decodeJob,不难想出,这两个任务之间一定有获取到资源的过程。而decodeJob包裹了engineJob,decodeJob是一个runnable,所以不难猜出会在线程池中网络请求资源,并且解码资源。下面我们就来看看是如何加载资源和解码的。

4.3 寻找资源加载器

此处不得不佩服框架考虑得的确很周全,我们需要加载资源,肯定就需要资源加载器,那么是不是只有一个加载器呢,当然不是,只有一个加载器的代码可能是我写的,哈哈哈。透露小秘密:框架会根据我们load传入的不同信息源,获取不同的资源加载器。下面我们来看一下流程。

获取url加载器.png

上一小节结束我们可以看到包裹了engineJob的decodeJob任务会在线程池中执行,那么不难想出decodeJob的run()肯定会被调用。

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      
      // 线程执行调用 run
      @Override
      public void run() {

        GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);

        DataFetcher<?> localFetcher = currentFetcher;
        try {
          //是否取消了当前请求
          if (isCancelled) {
            notifyFailed();
            return;
          }
          //执行
          runWrapped();
        } catch (CallbackException e) {

         .....//一些错误回调
      }
    }

分析runWrapped:

private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        //获取资源状态
        stage = getNextStage(Stage.INITIALIZE);
        //根据当前资源状态,获取资源执行器
        currentGenerator = getNextGenerator();
        //执行
        runGenerators();
        break;
      ...
    }
  }

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        //如果外部调用配置了资源缓存策略,那么返回 Stage.RESOURCE_CACHE
        //否则继续调用 Stage.RESOURCE_CACHE 执行。
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        //如果外部配置了源数据缓存,那么返回 Stage.DATA_CACHE
        //否则继续调用 getNextStage(Stage.DATA_CACHE)
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        //如果只能从缓存中获取数据,则直接返回 FINISHED,否则,返回SOURCE。
        //意思就是一个新的资源
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

通过上面代码可以知道,我们在找资源的执行器,这里由于我们没有在外部配置缓存策略所以,直接从源数据加载,看下面代码:

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);
    }
  }

由于我们什么都没有配置,返回的是 SourceGenerator 源数据执行器。继续下面代码执行:

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    //判断是否取消,是否开始
    //调用 DataFetcherGenerator.startNext() 判断是否是属于开始执行的任务
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
   
      ....
  }

上面代码先看 currentGenerator.startNext() 这句代码,DataFetcherGenerator 是一个抽象类,那么这里执行的实现类是哪一个,可以参考下面说明:

状态标记 作用 执行器
Stage.RESOURCE_CACHE 从磁盘中获取缓存的资源数据 ResourceCacheGenerator
Stage.DATA_CACHE 从磁盘中获取缓存的源数据 DataCacheGenerator
Stage.SOURCE 一次新的请求任务 SourceGenerator

因为这里我们没有配置缓存,那么直接看 SourceGenerator

@Override
  public boolean startNext() {
        ...
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //获取一个 ModelLoad 加载器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //使用加载器中的 fetcher 根据优先级加载数据
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

代码逻辑很简单,首先获取加载器,然后从加载器中使用fetcher来加载数据就可以了。

 List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear(); 
      //从 Glide 注册的 Model 来获取加载器(注册是在 Glide 初始化的时候通过 registry
       // .append()添加的)
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
     
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current =
          // 构建加载器。modelLoader是一个接口,需要知道是那个类的buildLoadData()来构建加载器。看看下面的分析。
            modelLoader.buildLoadData(model, width, height, options);
        //如果加载器器不为空,那么添加进临时缓存
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

获取到加载器之后,会通过加载器中的fetcher加载数据,然后我们进入loadData()一看,fetcher是一个接口(DataFetcher<Data>),实现类有一大堆(如下图),我们根本没法知道到底是哪个来加载数据。

fetcher实现类.png

此处就是框架设计巧妙的一点了,到底是怎么知道哪个fetcher呢?

此时我们要回到 Glide 初始的时候 通过 Registry.append() 添加的!!!!!! 下面我们来看看。

Glide(
      @NonNull Context context,
      @NonNull Engine engine,
      @NonNull MemoryCache memoryCache,
      @NonNull BitmapPool bitmapPool,
      @NonNull ArrayPool arrayPool,
      @NonNull RequestManagerRetriever requestManagerRetriever,
      @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      @NonNull RequestOptionsFactory defaultRequestOptionsFactory,
      @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
      @NonNull List<RequestListener<Object>> defaultRequestListeners,
      boolean isLoggingRequestOriginsEnabled,
      boolean isImageDecoderEnabledForBitmaps) {
    ......
    registry
        .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
        .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
        .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
        // 我们load传入的是一个url,所以我们的fetcher就是HttpGlideUrlLoader。
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
        .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
        .append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
        .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
        .append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
        /* Transcoders */
        .register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
        .register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
        .register(
            Drawable.class,
            byte[].class,
            new DrawableBytesTranscoder(
                bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
    
    ......
}

从Glide的构造方法中,我们找到了线索,registry.append()会根据我们load()传入不同类型的参数,为我们注册不同的fetcher。

// 说明:load传入参数类型----加载后的资源形式--------加载器----
append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

我们可以这么理解,我们传入的参数是URL,使用HttpGlideUrlLoader来加载,最终返回一个InputStream流。看了注册加载器之后,我们继续回到如何构建加载器的主路线上:

// LoadData<InputStream>就是加载器包裹,HttpUrlFetcher就是我们要找到真正的网络资源加载器!!!!
  @Override
  public LoadData<InputStream> buildLoadData(
      @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
    // spent parsing urls.
    GlideUrl url = model;
    if (modelCache != null) {
      url = modelCache.get(model, 0, 0);
      if (url == null) {
        modelCache.put(model, 0, 0, model);
        url = model;
      }
    }
    int timeout = options.get(TIMEOUT);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

此时就很清晰了,我们是通过HttpUrlFetcher来加载资源的,然后我们进入资源加载环节。

4.4 加载资源

从上一小节中,我们知道了我们传入的url是通过HttpUrlFetcher来加载资源。那么我们就进入HttpUrlFetcher的loadData()。

加载资源.png
@Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      //http 请求,返回一个 InputStream 输入流
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      //将 InputStream 以回调形式回调出去
      callback.onDataReady(result);
    } catch (IOException e) {
      callback.onLoadFailed(e);
    } finally {
     ...
    }
  }
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {
      throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
    } else {

      try {
        if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
          throw new HttpException("In re-direct loop");
        }
      } catch (URISyntaxException e) {
        // Do nothing, this is best effort.
      }
    }

    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);

    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();

    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    } 
    ...//抛的异常我们暂时先不管
  }

从上面代码我们可以看出 HttpURLConnection 作为 Glide 底层成网络请求的。请求成功之后直接返回的是一个输入流,最后会通过 onDataReady 回调到 DecodeJob的onDataFetcherReady 函数中。

此时我们已经从网络上获取到资源了,现在来看是如何回调的。

@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

这里会有 else 因为我们没有配置缓存,继续回调:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      ...
      @Override
      public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
          DataSource dataSource, Key attemptedKey) {
        this.currentSourceKey = sourceKey; //当前返回数据的 key
        this.currentData = data; //返回的数据
        this.currentFetcher = fetcher; //返回的数据执行器,这里可以理解为 HttpUrlFetcher
        this.currentDataSource = dataSource; //数据来源 url
        this.currentAttemptingKey = attemptedKey;
        if (Thread.currentThread() != currentThread) {
          runReason = RunReason.DECODE_DATA;
          callback.reschedule(this);
        } else {
          GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
          try {
            //解析返回回来的数据
            decodeFromRetrievedData();
          } finally {
            GlideTrace.endSection();
          }
        }
      }   
      ...
    }

至此,我们的数据已经从网络上加载完成。最后通过decodeFromRetrievedData()方法来解析inputstream流。

4.5 解析资源

从上一小节,我们拿到了资源流,下面我们来看看是如何解析资源的。

解析资源.png
  //解析返回的数据
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 调用 decodeFrom 解析 数据;HttpUrlFetcher , InputStream ,  currentDataSource
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    //解析完成后,通知下去
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
      DataSource dataSource) throws GlideException {
      ...  
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ....
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

  @SuppressWarnings("unchecked")
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    //获取当前数据类的解析器 LoadPath 
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    //通过 LoadPath 解析器来解析数据
    return runLoadPath(data, dataSource, path);
  }

  private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
      LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    
    //因为这里返回的是一个 InputStream 所以 这里拿到的是 InputStreamRewinder
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      //将解析资源的任务转移到 Load.path 方法中
      return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }

注意上面代码,为了解析数据首先构建一个 LoadPath, 然后创建一个 InputStreamRewinder 类型的 DataRewinder, 最终将数据解析的操作放到了 LoadPath.load 方法中 ,接下来看下 LoadPath.load 方法的具体逻辑操作:

public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
      int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
      
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      listPool.release(throwables);
    }
  }

  private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
      List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
   
    //遍历内部存储的 DecodePath 集合,通过他们来解析数据
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        //这里才是真正解析数据的地方
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
       ...
      }
     ...
    return result;
  }

最终通过path.decode()根据尺寸来解析inputstream流。

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //调用 decodeResourec 将数据解析成中间资源
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //解析完数据回调出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //转换资源为目标资源
    return transcoder.transcode(transformed, options);
  }

看看 decodeResource 怎么解析成中间资源的:

@NonNull
  private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
   ...
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
    ...
    }
  }

  @NonNull
  private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
    Resource<ResourceType> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          // 调用 ResourceDecoder.decode 解析数据
          result = decoder.decode(data, width, height, options);
        }

      } catch (IOException | RuntimeException | OutOfMemoryError e) {
                ...
      }
    return result;
  }

可以看到数据解析的任务最终是通过 DecodePath 来执行的, 它内部有三大步操作

  • 第一大步:deResource 将源数据解析成资源(源数据: InputStream, 中间产物: Bitmap)
  • 第二大步:调用 DecodeCallback.onResourceDecoded 处理资源
  • 第三大步:调用 ResourceTranscoder.transcode 将资源转为目标资源(目标资源类型: Drawable)

可以发现,通过上面的 decoder.decode 源码可知,它是一个接口,由于我们这里的源数据是 InputStream,所以,它的实现类是 StreamBitmapDecoder类 ,我们就来看下 它内部的解码过程:

@Override
  public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
      @NonNull Options options)
      throws IOException {

    // Use to fix the mark limit to avoid allocating buffers that fit entire images.
    final RecyclableBufferedInputStream bufferedStream;
    final boolean ownsBufferedStream;

    ....
      
    try {
      // 根据请求配置来对数据进行采样压缩,获取一个 Resource<Bitmap> 
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      ....
    }
  }

最终是通过downsampler去根据目标的宽高,来对输入数据流进行压缩。

回到主线中的三大步:

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //第一步: 调用 decodeResourec 将数据解析成中间资源 Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //第二步: 解析完数据回调出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //第三步: 转换资源为目标资源 Bitmap to Drawable
    return transcoder.transcode(transformed, options);
  }

解析完数据,看第二注释里面回调,最后会回调到 DecodeJob:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
      ...  
    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    } 
      ... 
}
@Synthetic
  @NonNull
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
    @SuppressWarnings("unchecked")
    //获取资源类型
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    //如果不是从磁盘资源中获取需要进行 transform 操作
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    ...
    //构建数据编码的策略
    final EncodeStrategy encodeStrategy;
    final ResourceEncoder<Z> encoder;
    if (decodeHelper.isResourceEncoderAvailable(transformed)) {
      encoder = decodeHelper.getResultEncoder(transformed);
      encodeStrategy = encoder.getEncodeStrategy(options);
    } else {
      encoder = null;
      encodeStrategy = EncodeStrategy.NONE;
    }
        
    //根据编码策略,构建缓存 Key
    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      if (encoder == null) {
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
      }
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          //源数据 key
          key = new DataCacheKey(currentSourceKey, signature);
          break;
          //... 省略 成吨的代码
      }
      //初始化编码管理者,用于提交内存缓存
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    //返回转换后的 Bitmap
    return result;
  }

此处说白了, onResourceDecoded 中, 主要是对中间资源做了如下的操作:

第一步:对资源进行了转换操作。比如 Fit_Center,CenterCrop, 这些都是在请求的时候配置的;

第二步:构建磁盘缓存的 key。

资源解析完成,已经存入磁盘,此处开始对资源继续转换。

4.6 资源转换

从上一节我们通过解码inputstream流得到Bitmap,然而我们显示需要将Bitmap转化成Drawable。下面我们继续接着上一小节看看资源是如何转换的。

资源转换.png
public class DecodePath<DataType, ResourceType, Transcode> { 
  //省略成吨的代码 ...  
  Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    //第一步: 调用 decodeResourec 将数据解析成中间资源 Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //第二步: 解析完数据回调出去
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    //第三步: 转换资源为目标资源 Bitmap to Drawable
    return transcoder.transcode(transformed, options);
  }
  // 省略成吨的代码 ... 
}

我们继续看第三步。通过源码可知,ResourceTranscoder 是一个接口,又因为解析完的数据是 Bitmap 所以它的实现类是 BitmapDrawableTranscoder ,最后看下它的 transcode 具体实现:

public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
  @Nullable
  @Override
  public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
      @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  } 
}
public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
    Initializable {

  private final Resources resources;
  private final Resource<Bitmap> bitmapResource;

  @Deprecated
  public static LazyBitmapDrawableResource obtain(Context context, Bitmap bitmap) {
    return
        (LazyBitmapDrawableResource)
            obtain(
                context.getResources(),
                BitmapResource.obtain(bitmap, Glide.get(context).getBitmapPool()));
  }

  @Deprecated
  public static LazyBitmapDrawableResource obtain(Resources resources, BitmapPool bitmapPool,Bitmap bitmap) {
    return (LazyBitmapDrawableResource) obtain(resources, BitmapResource.obtain(bitmap, bitmapPool));
  }

  @Nullable
  public static Resource<BitmapDrawable> obtain(
      @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }

  private LazyBitmapDrawableResource(@NonNull Resources resources,
      @NonNull Resource<Bitmap> bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

  @NonNull
  @Override
  public Class<BitmapDrawable> getResourceClass() {
    return BitmapDrawable.class;
  }

  // Get 方法返回了一个 BitmapDrawable 对象
  @NonNull
  @Override
  public BitmapDrawable get() {
    return new BitmapDrawable(resources, bitmapResource.get());
  }

  @Override
  public int getSize() {
    return bitmapResource.getSize();
  }

  @Override
  public void recycle() {
    bitmapResource.recycle();
  }

  @Override
  public void initialize() {
    if (bitmapResource instanceof Initializable) {
      ((Initializable) bitmapResource).initialize();
    }
  }
}

转化终于完成了 ,将我们解析到的 bitmap 存放到 LazyBitmapDrawableResource 内部, 然后外界通过 get 方法就可以获取到一个 BitmapDrawable 的对象了,解析完就到了展示数据了,请看下面代码:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  //解析返回的数据
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      //第一步: 调用 decodeFrom 解析 数据;HttpUrlFetcher , InputStream ,  currentDataSource
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    //第二步: 解析完成后,通知下去
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }  
 }

第一步就解析完了数据, 现在第二步执行 notifyEncodeAndRelease函数:

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    ...
    //通知调用层数据已经装备好了
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      //这里就是将资源磁盘缓存
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
     ...
    }
    //完成
    onEncodeComplete();
  }

  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    // 在 DecodeJob 的构建中, 我们知道这个 Callback 是 EngineJob
    callback.onResourceReady(resource, dataSource);
  }
}

可以看到上面的 DecodeJob.decodeFromRetrievedData 中主要做了三个处理:

第一个处理:解析返回回来的资源。

第二个处理:拿到解析的资源,如果配置了本地缓存,就缓存到磁盘。

第三个处理:通知上层资源准备就绪,可以使用了。

我们直接看 EngineJob 的 onResourceReady 回调函数:

 @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }

  @Synthetic
  void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
      ... 
      }
      engineResource = engineResourceFactory.build(resource, isCacheable);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }

    //回调上层 Engine 任务完成了
    listener.onEngineJobComplete(this, localKey, localResource);

    //遍历资源回调给 ImageViewTarget 
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }

通过上面 EngineJob 的 onResourceReady 回调函数 主要做了 两个处理:

第一个处理:通知上层任务完成。 

第二个处理:回调 ImageViewTarget 用于展示数据。

看下 listener.onEngineJobComplete 具体实现:

@SuppressWarnings("unchecked")
  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null) {
      resource.setResourceListener(key, this);
            //收到下游返回回来的资源,添加到活动缓存中
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    jobs.removeIfCurrent(key, engineJob);
  }

最终通知 ImageViewTarget, 看下具体操作:

//遍历资源回调给 ImageViewTarget 
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
 private class CallResourceReady implements Runnable {

    private final ResourceCallback cb;

    CallResourceReady(ResourceCallback cb) {
      this.cb = cb;
    }

    @Override
    public void run() {
      synchronized (EngineJob.this) {
        if (cbs.contains(cb)) {
         ...
          //返回准备好的资源
          callCallbackOnResourceReady(cb);
          removeCallback(cb);
        }
        decrementPendingCallbacks();
      }
    }
  }

我们可以看到 CallResourceReady 实现 Runnable ,当 entry.executor.execute 线程池执行的时候就会调用 run ,最后我们继续跟 callCallbackOnResourceReady函数:

  @Synthetic
  synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      //回调给 SingleRequest
      cb.onResourceReady(engineResource, dataSource);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }

SingleRequest onResourceReady 回调实现:

public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    loadStatus = null;
    ... 省略成吨的代码
    Object received = resource.get();
    if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
      releaseResource(resource);
      ... 省略成吨的代码
      onLoadFailed(exception);
      return;
    }

    if (!canSetResource()) {
      releaseResource(resource);
      status = Status.COMPLETE;
      return;
    }

   //当资源准备好的时候
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }

private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
      ... 省略成吨的代码
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        //回调给目标 ImageViewTarget 资源准备好了
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }
    //加载成功
    notifyLoadSuccess();
  }

这一步主要把准备好的资源回调给显示层!!!!!!! 终于快看到胜利的曙光了。

4.7 显示资源

资源终于完全准备好了,下面就是加载资源到控件上。

加载资源到控件上.png
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter { 
  ...
  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
 
  protected abstract void setResource(@Nullable Z resource);
  ...
}

  private void setResourceInternal(@Nullable Z resource) {
    //调用 setResource 函数,将资源显示出来
    setResource(resource);
    ...
  }

大家还记得么?在最开始构建的时候,我们知道只有调用 asBitmap 的时候实现类是 BitmapImageViewTarget,在这里的测试,并没有调用这个函数,所以它的实现类是 DrawableImageViewTarget,具体看下它内部实现:

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

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

  // Public API.
  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
}

这里看到抽象类中调用了 setResource ,子类实现并调用了 view.setImageDrawable(resource); 图片现在算是真正的显示出来了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容