Glide源码解析(1): 整体流程

1,339 阅读14分钟

Glide源码解析(1): 整体流程

前言

最近一段时间打算把一些常用的第三方开源库,自己好好地看一遍,毕竟纸上得来终觉浅,绝知此事要躬行啊。另外也是要给自己一点事去做,因为人一旦放松下来,脑子就容易滑丝,明明什么都没做,每天都觉得挺忙的,又玩个手机电脑一整天,休息没休息到,还更累了。我不想这样。

Glide是一个很常用的图片加载库,我从实习的时候就开始用了,看过一些原理,还模仿过一个加载库(自定义安卓图片懒加载,很有意思,但是真没系统性地去读它的源码,那现在就开始吧!

ps. 以下Glide版本为4.11.0。

使用例子

Glide最简单的使用就是下面三步了:

    fun useGlide(context: Context, url: String, imgView: ImageView) {
        Glide.with(context)         // RequestManager
            .load(url)              // RequestBuilder<Drawable>
            .into(imgView)
    }

其中with会生成一个RequestManager对象,它提供了对request的一些管理功能(例如: pauseRequests/resumeRequests):

    fun useGlide(context: Context, url: String, imgView: ImageView) {
        Glide.with(context)         // RequestManager
            .pauseRequests()
    }

而load会生成一个RequestBuilder对象,这一步可以设置缩放模式、圆角、占位图之类的:

    Glide.with(context)
        .load(url)
        .centerCrop() //centerCrop缩放模式
        .circleCrop() //裁剪为圆形
        .placeholder(ColorDrawable(Color.RED)) //占位drawable
        .into(imgView)

最后RequestBuilder的into方法会生成request,并发起请求,加载到imageView内,这里还会返回一个ViewTarget。

参考资料

刚开始点开Glide的源码还是唬住了我,有点懵逼,还是先看了一些资料后才继续往下看,下面也贴出来,希望读者也能少走些弯路:

Glide官方文档中文版

Glide系列之——初识Glide

Glide系列之——Glide对象创建及功能扩展

ps. 后面两篇文章我觉得写的不错,可惜tj了,后面部分的内容作者没写了。

整体流程

作为Glide解析的第一篇文章,我打算简单地追踪下整体流程,并绘制出一个流程图来,脑子里先有个概念,再去看源码具体实现,学习里面的精髓。

流程图

Glide流程图.png

ps. 感觉流程图比博客难弄,画的还烂。。。

with(context)

下面就来追踪第一步with的调用吧,先是到Glide中:

  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

这里分了两步,第一步是获得一个RequestManagerRetriever,再由RequestManagerRetriever去获取RequestManager,先看第一步:

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // ...
    return Glide.get(context).getRequestManagerRetriever();
  }

这里也分了两步,第一步是获得Glide对象,第二步才是获取RequestManagerRetriever,同样,先看第一步:

  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          // 初始化Glide单例
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

这里是一个DCL,并在其中初始化了Glide,然后返回了glide这个单例,至于Glide的初始化我觉得看到这就可以了,更深入地后面再讲了。

回到上面获取RequestManagerRetriever那,只是返回了Glide对象的requestManagerRetriever域:

  @NonNull
  public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
  }

再看最前面with中RequestManager的get方法:

  @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)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          // ...
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }

这里好像复杂点,我们可以先知道这个RequestManager会根据context的不同创建不同的对象,默认是Application级别的。

好了,小结下,with方法里面流程如下:

with(context) -> getRetriever(context) -> Glide.get(context) -> - |

RequestManager <- RequestManagerRetriever <- Glide <- - |

load(url)

上一步得到了一个RequestManager对象,下面继续看这个对象的load方法做了什么:

  @Override
  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对象,最后通过这个RequestBuilder去load:

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

额,所以就这?只是调用RequestBuilder的构造方法创建了个对象,然后调用了下它的loadGeneric方法,设置了些参数么。。

into(imgView)

既然前面并不复杂,那感觉复杂的就要来了,下面我们开始看into方法:

  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    // 先不管这个条件
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
        
      // 根据view的缩放模式设置option
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

这里先不管那个if的逻辑,代码流程进入到了下面这个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 request = buildRequest(target, targetListener, options, callbackExecutor);

    // 判断了下要加载的目标imageView旧的请求是否和新的一致,做出处理
    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;
  }

我们就看新创建的请求是如何加载的,主要在requestManager的track方法:

  // 注意加了同步锁
  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  
  public void track(@NonNull Target<?> target) {
    targets.add(target);
  }
  
  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);
    }
  }

requestManager的track方法做了两步操作,第一个就是把target加到targets里面,然后通过requestTracker去run request:

  // requestTracker中
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    
    // 判断了下UI界面是否被pause了,是的话就加到pendingRequests中等待执行
    if (!isPaused) {
      request.begin();
    } else {
      request.clear();
      pendingRequests.add(request);
    }
  }

终于啊,一路追踪下来看到了request的begin方法,这里终于要开始网络请求了!我们下面新开一小节。

request.begin()

看到这又有点懵了,这个Request到底是哪个实现类?这里有三个实现类:

  • ErrorRequestCoordinator
  • SingleRequest
  • ThumbnailRequestCoordinator

只能回到request创建的地方追踪下:

  Request request = buildRequest(target, targetListener, options, callbackExecutor);
  private Request buildRequest(...) {
    return buildRequestRecursive(...);
  }

好家伙,Recursive都出来了么?这也能递归,看下代码:

  private Request buildRequestRecursive(...) {

    // 处理失败的协调器?
    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }

    // 略缩图请求?
    Request mainRequest = buildThumbnailRequestRecursive(...);

    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(...);
    
    // 这里把略缩图请求和失败请求都给了errorRequestCoordinator(失败协调器)
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

这里代码很长,但是东西不多,大致就是一个协调的request里面又包了两个request,正常的话还是执行mainRequest:

  private Request buildThumbnailRequestRecursive(...) {
      // 套娃太长,后面研究,看重点
      Request fullRequest = obtainRequest(...);
  }
  
  private Request obtainRequest(...) {
    return SingleRequest.obtain(...);
  }

buildThumbnailRequestRecursive方法里面有点复杂,但是几个条件最后都是通过obtainRequest来生成request对象的,最后生成的对象是SingleRequest。

那终于可以看下我们这小节的流程到底是怎么执行的,下面是SingleRequest的begin方法:

  @Override
  public void begin() {
    synchronized (requestLock) {
      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)) {
        // 已获得view宽高,内部开始执行代码
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        // 还未获得宽高,传入一个SizeReadyCallback,等onMeasure结束(获得宽高)后执行onSizeReady(这个方法就从接口来的)
        target.getSize(this);
      }

      // 这里onLoadStarted注释写的: 生命周期的callBack,点进去看好像有设置placeHoldeer
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
    }
  }

代码有点复杂了,但就三点,一个是异常情况,第二个是等view measure完成获取宽高去执行onSizeReady方法,第三个是通知onLoadStarted。

下面就看下但view宽高确定后,如何操作的:

  // A callback method that should never be invoked directly. 注释注意下
  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      // 修改状态到RUNNING
      if (status != Status.WAITING_FOR_SIZE) { return; }
      status = Status.RUNNING;

      float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

      // 原来加载交到了engine去执行
      loadStatus = engine.load(...);
      
      // 这里只对同步操作有关系,用于测试是否再主线程执行,不用管它
      if (status != Status.RUNNING) { loadStatus = null; }
    }
  }

看完上面代码,也就是说Glide先等view measure完成获取宽高后,将加载任务交到了engine去执行,里面可能是异步执行的。在engine执行过程中,onLoadStarted方法还会做一些操作,例如通知、设置placeHolder之类的。

engine.load(...)

上面流程讲到了engine里面,感觉东西不会少,新开一小节。下面看下engine里面的load方法:

  public <R> LoadStatus load(...) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    
    // 缓存用的key?
    EngineKey key = keyFactory.buildKey(...);

    // 加锁,取内存缓存
    EngineResource<?> memoryResource;
    synchronized (this) {
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
      
      // 没有拿到内存缓存
      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(...);
      }
    }

    // 拿到内存缓存
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }

内存缓存我们后面再讲,先看流程走势,这里没有内存缓存的话就到了waitForExistingOrStartNewJob方法:

private <R> LoadStatus waitForExistingOrStartNewJob(...) {
    // 先获取了下现在执行的任务
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      // 延迟执行?
      current.addCallback(cb, callbackExecutor);
      return new LoadStatus(cb, current);
    }

    // 下载任务?好像不对
    EngineJob<R> engineJob = engineJobFactory.build(...);
    // 解码任务?好像逻辑都在它里面
    DecodeJob<R> decodeJob = decodeJobFactory.build(...);

    jobs.put(key, engineJob);

    // 增加回调
    engineJob.addCallback(cb, callbackExecutor);
    // 启动engineJob
    engineJob.start(decodeJob);

    return new LoadStatus(cb, engineJob);
  }

上面代码也就是说engine实际把任务分成了两部分,engineJob和decodeJob,并将decodeJob传到engineJob中,启动engineJob完成加载。

那流程就来到了EngineJob的start方法:

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    // 根据是否加载缓存选择线程池
    GlideExecutor executor =
        decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }
  
  private GlideExecutor getActiveSourceExecutor() {
    // 上面一个,这里又来三个?
    return useUnlimitedSourceGeneratorPool
        ? sourceUnlimitedExecutor
        : (useAnimationPool ? animationExecutor : sourceExecutor);
  }

这就有点懵了,这里出现了四个线程池:

  • diskCacheExecutor,硬盘缓存线程池
  • sourceUnlimitedExecutor,无限资源线程池?
  • animationExecutor,动画线程池?
  • sourceExecutor,资源线程池?

不过好在他们四个都是GlideExecutor,那就看下GlideExecutor的execute做了什么:

  public void execute(@NonNull Runnable command) {
    // GlideExecutor里面都有代理线程池完成
    delegate.execute(command);
  }
  
  public GlideExecutor build() {
      if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException(...; }
      
      // 线程池的相关参数
      ThreadPoolExecutor executor =
          new ThreadPoolExecutor(
              corePoolSize,
              maximumPoolSize,
              /*keepAliveTime=*/ threadTimeoutMillis,
              TimeUnit.MILLISECONDS,
              new PriorityBlockingQueue<Runnable>(),
              new DefaultThreadFactory(name, uncaughtThrowableStrategy, preventNetworkOperations));

      if (threadTimeoutMillis != NO_THREAD_TIMEOUT) {
        executor.allowCoreThreadTimeOut(true);
      }

      return new GlideExecutor(executor);
    }

呃,好像和普通线程池没什么不一样,所以核心内容实际是在decodeJob里面?那就看下decodeJob的run方法做了什么:

  public void run() {
    // 不知道什么意思,日志记录器么?
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    
    // 获取数据
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      
      // 核心代码
      runWrapped();
    } catch (CallbackException e) {
      // 不受Glide控制的异常
      throw e;
    } catch (Throwable t) {
      // 失败回调,可能不安全?
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      
      // 为什么多此一举。。。
      if (!isCancelled) {
        throw t;
      }
      throw t;
    } finally {
      // 加载完清理?
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  }

run方法里面就是在try和catch中执行了runWrapped方法,处理了下异常以及finally情形。下面看runWrapped方法:

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

真复杂,这里的意思感觉就是会执行好几遍,runReason控制当前执行到哪一步,那我们就从INITIALIZE看下去,这里先获取了一个currentGenerator:

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

不是很明白它的作用,继续看INITIALIZE里面的runGenerators方法:

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    
    // 所以核心代码就是startNext方法了
    while (!isCancelled
        && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      
      // 新状态,新currentGenerator
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      // 重新定时?
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    
    // 到这就是失败了
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }

上面代码指示说是currentGenerator的startNext方法起效,也就是说是下面几个DataFetcherGenerator在执行代码:

  • ResourceCacheGenerator,从采样、转换过的缓存中取

    Generates DataFetchers from cache files containing downsampled/transformed resource data.

  • DataCacheGenerator,从未修改的缓存中取

    Generates DataFetchers from cache files containing original unmodified source data.

  • SourceGenerator,从原始数据中取

    Generates DataFetchers from original source data using registered ModelLoaders and the model provided for the load.

这里就不深入了,大致就是取数据的一些流程,下一节再来看下取网络数据的,这里我们先看下runWrapped中DECODE_DATA里面的decodeFromRetrievedData方法:

  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 处理数据
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    
    if (resource != null) {
      // 通知获取数据完成
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

在这里通过decodeFromData方法处理了currentGenerator中得到的数据,生成resource资源,并用notifyEncodeAndRelease方法通知数据获取完成:

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    // 关键生命周期函数,通知Encode完成
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    
    // 做一些释放资源的操作
    onEncodeComplete();
  }

这里就直接到了notifyComplete方法,通知资源获取结束了:

  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }

这里通过callback传递出去,那就是SingleRequest的事了。

SourceGenerator

前面虽然把engineJob如何把数据获取并传递出去讲完了,但是获取数据的部分省略了,这节来简单看下,看SourceGenerator的startNext方法:

  @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;
        
        // 在这里加载
        startNextLoad(loadData);
      }
    }
    return started;
  }

这里经过一系列判断,最后走到了startNextLoad方法:

  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

先不管其他的,这里加载的对象就是loadData.fetcher了,也就是DataFetcher类,DataFetcher有很多实现类,下面是几个例子:

  • StreamLocalUriFetcher:用于从本地文件系统加载图片。
  • HttpGlideUrlFetcher:用于从网络加载图片。
  • FileDescriptorLocalUriFetcher:用于从本地文件描述符加载图片。
  • AssetUriFetcher:用于从应用程序的 assets 文件夹加载图片。
  • MediaStoreImageThumbFetcher:用于从 MediaStore 中加载缩略图。
  • MediaStoreVideoThumbFetcher:用于从 MediaStore 中加载视频缩略图。

我感觉我们要的是HttpGlideUrlFetcher,下面看下它的loadData方法:

  @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) {
      callback.onLoadFailed(e);
    } finally {
      //...
    }
  }

最终下载任务到了loadDataWithRedirects方法:

  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去下载
    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);
    } else if (isHttpRedirect(statusCode)) {
      String redirectUrlString = urlConnection.getHeaderField("Location");
      if (TextUtils.isEmpty(redirectUrlString)) {
        throw new HttpException("Received empty or null redirect url");
      }
      URL redirectUrl = new URL(url, redirectUrlString);
      
      // 清理
      cleanup();
      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    } else {
      throw new HttpException(urlConnection.getResponseMessage(), statusCode);
    }
  }

看到这里就不深入了吧,毕竟这篇博客讲的是整体流程,到urlConnection去下载我觉得就差不多了。

onResourceReady

上上节我们讲到engineJob获取到资源后,会通过回调,执行SingleRequest的onResourceReady方法,下面我们看下这个方法:

  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
        
        // 没获取到资源
        if (resource == null) {
          GlideException exception = new GlideException(...);
          onLoadFailed(exception);
          return;
        }

        // 有resource,但是收到空资源
        Object received = resource.get();
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
          toRelease = resource;
          this.resource = null;
          GlideException exception = new GlideException(...);
          onLoadFailed(exception);
          return;
        }

        // 有效资源
        if (!canSetResource()) {
          toRelease = resource;
          this.resource = null;
          // We can't put the status to complete before asking canSetResource().
          status = Status.COMPLETE;
          return;
        }

        // 额,这里还有个多态方法
        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      }
    } finally {
      if (toRelease != null) {
        // 设置完回收资源?
        engine.release(toRelease);
      }
    }
  }

接着看下另一个onResourceReady方法:

  @GuardedBy("requestLock")
  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      
      // 通知自定义监听?
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      
      // 回调监听?
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      // 可以拦截图像的设置?
      if (!anyListenerHandledUpdatingTarget) {
        // 动画?
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        // 这里才加载到目标去
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
  }

看了下notifyLoadSuccess好像不是通知view更新的,仅仅是对request的Coordinator的通知:

  @GuardedBy("requestLock")
  private void notifyLoadSuccess() {
    if (requestCoordinator != null) {
      // 前面那个request的Coordinator
      requestCoordinator.onRequestSuccess(this);
    }
  }

所以是在target的onResourceReady方法里面去更新图像的,这里target又很多类,我觉得我们需要看得是ImageViewTarget:

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      // 不需要动画
      setResourceInternal(resource);
    } else {
      // 带动画
      maybeUpdateAnimatable(resource);
    }
  }

我们就看下不带动画的maybeUpdateAnimatable方法:

  private void setResourceInternal(@Nullable Z resource) {
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }
  
  // 执行动画
  private void maybeUpdateAnimatable(@Nullable Z resource) {
    if (resource instanceof Animatable) {
      animatable = (Animatable) resource;
      animatable.start();
    } else {
      animatable = null;
    }
  }

  protected abstract void setResource(@Nullable Z resource);

这里setResource居然是个抽象方法,那就看下ImageViewTarget的实现类,又有挺多个,我就拿DrawableImageViewTarget看一下:

  @Override
  protected void setResource(@Nullable Drawable resource) {
    // 通过ImageView的setImageDrawable设置图片
    view.setImageDrawable(resource);
  }

最终是通过ImageView去设置resource了。

小结

终于把Glide的流程走了一遍了,有点马虎,但是我觉得还是挺有用,后面会多写几篇详细看看。