Glide源码学习之into

135 阅读12分钟

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

以上是Glide 的基本用法,其中Glide.with方法返回一个RequestManager对象,而load方法返回一个RequestBuilder对象,所以最终会进入到RequestBuilder中的into方法。

1、构建一个ImageViewTarget

RequestBuilder.java

@NonNull

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {

  //主线程以及非空判断

  Util.assertMainThread();

  Preconditions.checkNotNull(view);

  ///读取ImageView的图片缩放模式

  BaseRequestOptions<?> requestOptions = this;

  if (!requestOptions.isTransformationSet()

      && requestOptions.isTransformationAllowed()

      && view.getScaleType() != null) {

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

}

glideContext.buildImageViewTarget(view, transcodeClass)是构建出一个ImageViewTarget,是专门用来显示图片的。

2、创建一个图片请求,并将新创建的ImageViewTarget和图片请求SingleRequest 设置到RequestManager中,其实是将其添加到请求队列中。

RequestBuilder.java

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

  //target中是否已有图片请求

  Request previous = target.getRequest();

  if (request.isEquivalentTo(previous)

      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {

    // If the request is completed, beginning again will ensure the result is re-delivered,

 // triggering RequestListeners and Targets. If the request is failed, beginning again will

 // restart the request, giving it another chance to complete. If the request is already

 // running, we can let it continue running without interruption.

 if (!Preconditions.checkNotNull(previous).isRunning()) {

      // Use the previous request rather than the new one to allow for optimizations like skipping

 // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions

 // that are done in the individual Request.

 previous.begin();

    }

    return target;

  }

  //清理当前target中当前已有的请求 

  requestManager.clear(target);

  //为target设置最新的图片请求

  target.setRequest(request);

  //将当前请求添加到带请求列表中

  requestManager.track(target, request);



  return target;

}

Request request = buildRequest(target, targetListener, options, callbackExecutor);是构建了一个请求,用来请求图片资源。具体的实现类是SingleRequest对象。

然后将该target和请求设置到RequestManager中。

RequestManager.java

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {

  targetTracker.track(target);

  requestTracker.runRequest(request);

}

TargetTracker.java

public final class TargetTracker implements LifecycleListener {

  private final Set<Target<?>> targets =

      Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());



  //添加一个新的target

  public void track(@NonNull Target<?> target) {

    targets.add(target);

  }



  public void untrack(@NonNull Target<?> target) {

    targets.remove(target);

  }



  @Override

  public void onStart() {

    for (Target<?> target : Util.getSnapshot(targets)) {

      target.onStart();

    }

  }



  @Override

  public void onStop() {

    for (Target<?> target : Util.getSnapshot(targets)) {

      target.onStop();

    }

  }



  @Override

  public void onDestroy() {

    for (Target<?> target : Util.getSnapshot(targets)) {

      target.onDestroy();

    }

  }



  @NonNull

  public List<Target<?>> getAll() {

    return Util.getSnapshot(targets);

  }



  public void clear() {

    targets.clear();

  }

}

3、启动图片请求。

RequestTracker.java

public class RequestTracker {

  private static final String TAG = "RequestTracker";



 private final Set<Request> requests =

      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());

 

 @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")

  private final List<Request> pendingRequests = new ArrayList<>();



  private boolean isPaused;



  //添加一个新的请求并尝试启动该请求

 public void runRequest(@NonNull Request request) {

    //requests是请求的执行队列

    requests.add(request);

    if (!isPaused) {

      //如果没有暂停的话,启动请求

      request.begin();

    } else {

      request.clear();

      if (Log.isLoggable(TAG, Log.VERBOSE)) {

        Log.v(TAG, "Paused, delaying request");

      }

      //pendingRequests是请求的等待队列

      pendingRequests.add(request);

    }

  }



  @VisibleForTesting

  void addRequest(Request request) {

    requests.add(request);

  }



  /**

 * Stops tracking the given request, clears, and recycles it, and returns {@code true} if the

 * request was removed or invalid or {@code false} if the request was not found.

 */

 public boolean clearAndRemove(@Nullable Request request) {

    if (request == null) {

      // If the Request is null, the request is already cleared and we don't need to search further

 // for its owner.

 return true;

    }

    boolean isOwnedByUs = requests.remove(request);

    // Avoid short circuiting.

 isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;

    if (isOwnedByUs) {

      request.clear();

    }

    return isOwnedByUs;

  }



  /** Returns {@code true} if requests are currently paused, and {@code false} otherwise. */

 public boolean isPaused() {

    return isPaused;

  }



  /** Stops any in progress requests. */

 public void pauseRequests() {

    isPaused = true;

    for (Request request : Util.getSnapshot(requests)) {

      if (request.isRunning()) {

        // Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking

 // in the UI, while still making sure that any in progress parts of requests are immediately

 // stopped.

 request.pause();

        pendingRequests.add(request);

      }

    }

  }



  /** Stops any in progress requests and releases bitmaps associated with completed requests. */

 public void pauseAllRequests() {

    isPaused = true;

    for (Request request : Util.getSnapshot(requests)) {

      if (request.isRunning() || request.isComplete()) {

        request.clear();

        pendingRequests.add(request);

      }

    }

  }



  /** Starts any not yet completed or failed requests. */

 public void resumeRequests() {

    isPaused = false;

    for (Request request : Util.getSnapshot(requests)) {

      // We don't need to check for cleared here. Any explicit clear by a user will remove the

 // Request from the tracker, so the only way we'd find a cleared request here is if we cleared

 // it. As a result it should be safe for us to resume cleared requests.

 if (!request.isComplete() && !request.isRunning()) {

        request.begin();

      }

    }

    pendingRequests.clear();

  }



  /**

 * Cancels all requests and clears their resources.

 *

 * <p>After this call requests cannot be restarted.

 */

 public void clearRequests() {

    for (Request request : Util.getSnapshot(requests)) {

      // It's unsafe to recycle the Request here because we don't know who might else have a

 // reference to it.

 clearAndRemove(request);

    }

    pendingRequests.clear();

  }



  /** Restarts failed requests and cancels and restarts in progress requests. */

 public void restartRequests() {

    for (Request request : Util.getSnapshot(requests)) {

      if (!request.isComplete() && !request.isCleared()) {

        request.clear();

        if (!isPaused) {

          request.begin();

        } else {

          // Ensure the request will be restarted in onResume.

 pendingRequests.add(request);

        }

      }

    }

  }



  @Override

  public String toString() {

    return super.toString() + "{numRequests=" + requests.size() + ", isPaused=" + isPaused + "}";

  }

}

在最开始的时候buildRequest(target, targetListener, options, callbackExecutor);创建的是SingleRequest对象,所以上面的request.begin();方法其实是执行SingleRequest中的begin方法。

3.1 、首先是获取图片的大小。

SingleRequest.java

@Override

public void begin() {

  synchronized (requestLock) {

    assertNotCallingCallbacks();

    stateVerifier.throwIfRecycled();

    startTime = LogTime.getLogTime();

    if (model == null) {

      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

        width = overrideWidth;

        height = overrideHeight;

      }

      // Only log at more verbose log levels if the user has set a fallback drawable, because

 // fallback Drawables indicate the user expects null models occasionally.

 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 we're restarted after we're complete (usually via something like a notifyDataSetChanged

 // that starts an identical request into the same Target or View), we can simply use the

 // resource and size we retrieved the last time around and skip obtaining a new size, starting

 // a new load etc. This does mean that users who want to restart a load because they expect

 // that the view size has changed will need to explicitly clear the View or Target before

 // starting the new load.

 if (status == Status.COMPLETE) {

      onResourceReady(

          resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);

      return;

    }



    // Restarts for requests that are neither complete nor running can be treated as new requests

 // and can run again from the beginning.



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

    }

  }

}

该函数中的重点在上面标红的部分,如果指定的宽高是有效的宽高,会调用onSizeReady(overrideWidth, overrideHeight)方法,否则,会执行target.getSize(this)方法,再次测量宽高。然而,getSize方法最终也会调用onSizeReady方法,所以下面我们需要关注该方法。

3.2 、 在引擎中尝试加载图片资源。

SingleRequest.java

@Override

public void onSizeReady(int width, int height) {

  stateVerifier.throwIfRecycled();

  synchronized (requestLock) {

    if (IS_VERBOSE_LOGGABLE) {

      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));

    }

    if (status != Status.WAITING_FOR_SIZE) {

      return;

    }

    status = Status.RUNNING;



    float sizeMultiplier = requestOptions.getSizeMultiplier();

    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);

    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);



    if (IS_VERBOSE_LOGGABLE) {

      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));

    }

    loadStatus =

        engine.load(

            glideContext,

            model,

            requestOptions.getSignature(),

            this.width,

            this.height,

            requestOptions.getResourceClass(),

            transcodeClass,

            priority,

            requestOptions.getDiskCacheStrategy(),

            requestOptions.getTransformations(),

            requestOptions.isTransformationRequired(),

            requestOptions.isScaleOnlyOrNoTransform(),

            requestOptions.getOptions(),

            requestOptions.isMemoryCacheable(),

            requestOptions.getUseUnlimitedSourceGeneratorsPool(),

            requestOptions.getUseAnimationPool(),

            requestOptions.getOnlyRetrieveFromCache(),

            this,

            callbackExecutor);



    // This is a hack that's only useful for testing right now where loads complete synchronously

 // even though under any executor running on any thread but the main thread, the load would

 // have completed asynchronously.

 if (status != Status.RUNNING) {

      loadStatus = null;

    }

    if (IS_VERBOSE_LOGGABLE) {

      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));

    }

  }

}

3.3、加载时会根据图片的宽高等信息计算一个key值,然后根据这个key值去活动缓存和内存缓存中查找是否有图片的缓存,如果有。则会调用SingleRequest中的onResourceReady方法尝试显示图片。

Engine.java

public <R> LoadStatus load(

    GlideContext glideContext,

    Object model,

    Key signature,

    int width,

    int height,

    Class<?> resourceClass,

    Class<R> transcodeClass,

    Priority priority,

    DiskCacheStrategy diskCacheStrategy,

    Map<Class<?>, Transformation<?>> transformations,

    boolean isTransformationRequired,

    boolean isScaleOnlyOrNoTransform,

    Options options,

    boolean isMemoryCacheable,

    boolean useUnlimitedSourceExecutorPool,

    boolean useAnimationPool,

    boolean onlyRetrieveFromCache,

    ResourceCallback cb,

    Executor callbackExecutor) {

  long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;



  //得到该次请求的一个key值,后续缓存的获取都需要依赖该key值,计算时会结合宽高等信息

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

    }

  }



  // Avoid calling back while holding the engine lock, doing so makes it easier for callers to deadlock.

 //如果找到了资源,回调显示

 cb.onResourceReady(memoryResource, DataSource. MEMORY_CACHE , false); 

  return null;

}







@Nullable

private EngineResource<?> loadFromMemory(

    EngineKey key, boolean isMemoryCacheable, long startTime) {

  if (!isMemoryCacheable) {

    return null;

  }

  //先查找活动缓存中是否有该图片的数据

  EngineResource<?> active = loadFromActiveResources(key);

  if (active != null) {

    if (VERBOSE_IS_LOGGABLE) {

      logWithTimeAndKey("Loaded resource from active resources", startTime, key);

    }

    return active;

  }

  //在查找内存缓存中是否有该图片的数据

  EngineResource<?> cached = loadFromCache(key);

  if (cached != null) {

    if (VERBOSE_IS_LOGGABLE) {

      logWithTimeAndKey("Loaded resource from cache", startTime, key);

    }

    return cached;

  }



  return null;

}

已经有了内存缓存,为什么还要设置活动缓存?

内存缓存是基于LRU的,为了防止图片内存缓存过多时,正在页面中显示的图片资源被释放,所以才需要在内存缓存之上再设置活动缓存。

3.3.1、当拿到图片资源,回调到SingleRequest中时,最终会传递到target中进行处理。

SingleRequest.java

@Override

public void onResourceReady(

    Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {

  stateVerifier.throwIfRecycled();

  Resource<?> toRelease = null;

  try {

    synchronized (requestLock) {

      loadStatus = null;



      Object received = resource.get();



      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, isLoadedFromAlternateCacheKey); 

    }

  } finally {

    if (toRelease != null) {

      engine.release(toRelease);

    }

  }

}





@SuppressWarnings({"deprecation", "PMD.UnusedFormalParameter"})

@GuardedBy("requestLock")

private void onResourceReady(

    Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {

  // 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();

}
3.3.2、匹配到缓存后,调用ImageViewTarget中的onResourceReady显示图片。

ImageViewTarget.java

@Override

public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {

  if (transition == null || !transition.transition(resource, this)) {

    setResourceInternal(resource); 

  } else {

    maybeUpdateAnimatable(resource);

  }

}



private void setResourceInternal(@Nullable Z resource) {

  // Order matters here. Set the resource first to make sure that the Drawable has a valid and

 // non-null Callback before starting it.

 setResource(resource); 

  maybeUpdateAnimatable(resource);

}

此处以BitmapImageViewTarget为例,可以看到最终将 bitmap设置到ImageView中。

public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {

  // Public API.

 @SuppressWarnings("WeakerAccess")

  public BitmapImageViewTarget(ImageView view) {

    super(view);

  }



  /** @deprecated Use {@link #waitForLayout()} instead. */

 // Public API.

 @SuppressWarnings({"unused", "deprecation"})

  @Deprecated

  public BitmapImageViewTarget(ImageView view, boolean waitForLayout) {

    super(view, waitForLayout);

  }



  /**

 * Sets the {@link android.graphics.Bitmap} on the view using {@link

 * android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.

 *

 * @param resource The bitmap to display.

 */

 @Override

  protected void setResource(Bitmap resource) {

    view.setImageBitmap(resource);

  }

}
3.3.3、没有根据key值,没有匹配到缓存,即memoryResource为空,则会创建一个job去加载图片资源。

Engine.java

private <R> LoadStatus waitForExistingOrStartNewJob(

    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,

    EngineKey key,

    long startTime) {



  //注释 ①

  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);

  if (current != null) {

    current.addCallback(cb, callbackExecutor);

    if (VERBOSE_IS_LOGGABLE) {

      logWithTimeAndKey("Added to existing load", startTime, key);

    }

    return new LoadStatus(cb, current);

  }



  EngineJob<R> engineJob =

      engineJobFactory.build(

          key,

          isMemoryCacheable,

          useUnlimitedSourceExecutorPool,

          useAnimationPool,

          onlyRetrieveFromCache);



  DecodeJob<R> decodeJob =

      decodeJobFactory.build(

          glideContext,

          model,

          key,

          signature,

          width,

          height,

          resourceClass,

          transcodeClass,

          priority,

          diskCacheStrategy,

          transformations,

          isTransformationRequired,

          isScaleOnlyOrNoTransform,

          onlyRetrieveFromCache,

          options,

          engineJob);



  jobs.put(key, engineJob);



  engineJob.addCallback(cb, callbackExecutor);

  engineJob.start(decodeJob);



  if (VERBOSE_IS_LOGGABLE) {

    logWithTimeAndKey("Started new load", startTime, key);

  }

  return new LoadStatus(cb, engineJob);

}

首先会在注释①处检测该key是否有任务正在执行,如果有正在执行的任务,直接返回。

EngineJob中有一系列的线程池。

3.3.3.1、创建EngineJob和DecodeJob,尝试请求资源。

EngineJob.java

public synchronized void start(DecodeJob<R> decodeJob) {

  this.decodeJob = decodeJob;

  GlideExecutor executor =

      decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();

  executor.execute(decodeJob); 

}
3.3.3.2、此处会根据设定的缓存策略找到合适的Generator,默认是SourceGenerator。

DecodeJob.java

@Override

public void run() {

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

 DataFetcher<?> localFetcher = currentFetcher;

  try {

    if (isCancelled) {

      notifyFailed();

      return;

    }

    runWrapped(); 

  } catch (CallbackException e) {

 throw e;

  } catch (Throwable t) {

    // When we're encoding we've already notified our callback and it isn't safe to do so again.

 if (stage != Stage.ENCODE) {

      throwables.add(t);

      notifyFailed();

    }

    if (!isCancelled) {

      throw t;

    }

    throw t;

  } finally {

 if (localFetcher != null) {

      localFetcher.cleanup();

    }

    GlideTrace.endSection();

  }

}



private void runWrapped() {

  switch (runReason) {

    case INITIALIZE:

      stage = getNextStage(Stage.INITIALIZE);

      currentGenerator = getNextGenerator(); 

      runGenerators(); 

      break;

    case SWITCH_TO_SOURCE_SERVICE:

      runGenerators();

      break;

    case DECODE_DATA:

      decodeFromRetrievedData();

      break;

    default:

      throw new IllegalStateException("Unrecognized run reason: " + runReason);

  }

}





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

  }

}



private void runGenerators() {

  currentThread = Thread.currentThread();

  startFetchTime = LogTime.getLogTime();

  boolean isStarted = false;

  while (!isCancelled

 && currentGenerator != null

 && !(isStarted = currentGenerator.startNext()  )) {

 stage = getNextStage(stage);

 currentGenerator = getNextGenerator();



 if (stage == Stage. SOURCE ) {

 reschedule();

 return;

 }

 }

  // We've run out of stages and generators, give up.

 if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {

    notifyFailed();

  }

}



//缓存策略

private Stage getNextStage(Stage current) {

  switch (current) {

    case INITIALIZE:

      return diskCacheStrategy.decodeCachedResource()

          ? Stage.RESOURCE_CACHE

 : getNextStage(Stage.RESOURCE_CACHE);

    case RESOURCE_CACHE:

      return diskCacheStrategy.decodeCachedData()

          ? Stage.DATA_CACHE

 : getNextStage(Stage.DATA_CACHE);

    case DATA_CACHE:

      // Skip loading from source if the user opted to only retrieve the resource from cache.

 return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;

    case SOURCE:

    case FINISHED:

      return Stage.FINISHED;

    default:

      throw new IllegalArgumentException("Unrecognized stage: " + current);

  }

}

在getNextGenerator方法中,根据设置的缓存策略,执行对应的Generator。如果没有设置,默认会返回SourceGenerator。

3.3.2.3、在SourceGenerator中去获取图片数据

SourceGenerator.java

@Override

public boolean startNext() {

  if (dataToCache != null) {

    Object data = dataToCache;

    dataToCache = null;

    cacheData(data);

  }



  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {

    return true;

  }

  sourceCacheGenerator = null;



  loadData = null;

  boolean started = false;

  while (!started && hasNextModelLoader()) {

 loadData = helper.getLoadData().get(loadDataListIndex++); 

    if (loadData != null

        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())

            || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {

      started = true;

      startNextLoad(loadData);

    }

  }

  return started;

}
3.3.3.4、此处开始涉及到网络请求

DecodeHelper

List<LoadData<?>> getLoadData() {

  if (!isLoadDataSet) {

    isLoadDataSet = true;

    loadData.clear();

    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);

    //noinspection ForLoopReplaceableByForEach to improve perf

 for (int i = 0, size = modelLoaders.size(); i < size; i++) {

      ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);

 LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); 

      if (current != null) {

        loadData.add(current);

      }

    }

  }

  return loadData;

}

HttpGlideUrlLoader

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

 public static final Option<Integer> TIMEOUT =

      Option.memory("com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500);



  @Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;



  public HttpGlideUrlLoader() {

    this(null);

  }



  public HttpGlideUrlLoader(@Nullable ModelCache<GlideUrl, GlideUrl> modelCache) {

    this.modelCache = modelCache;

  }



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

 }



  @Override

  public boolean handles(@NonNull GlideUrl model) {

    return true;

  }



  /** The default factory for {@link HttpGlideUrlLoader}s. */

 public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {

    private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<>(500);



    @NonNull

    @Override

    public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {

      return new HttpGlideUrlLoader(modelCache);

    }



    @Override

    public void teardown() {

      // Do nothing.

 }

  }

}
3.3.3.5、在HttpGlideUrlLoader中通过网络获取到LoadData之后,紧接着会进入到SourceGenerator中的startNextLoad中,尝试真正的去获取图片数据。

SourceGenerator.java

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

          }

        }

      });

}



void onDataReadyInternal(LoadData<?> loadData, Object data) {

  DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();

  if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {

    dataToCache = data;

    // We might be being called back on someone else's thread. Before doing anything, we should

 // reschedule to get back onto Glide's thread.

 cb.reschedule();

  } else {

    cb.onDataFetcherReady( 

 loadData.sourceKey, 

 data, 

 loadData.fetcher, 

 loadData.fetcher.getDataSource(), 

 originalKey); 

  }

}

loadData.fetcher中的fetcher就是下面的HttpUrlFetcher。

3.3.3.6、在HttpUrlFetcher中获取到图片的输入流。然后通过回调的方式将输入流返回到SourceGenerator中的onDataReady中。

HttpUrlFetcher

public class HttpUrlFetcher implements DataFetcher<InputStream> {

  private static final String TAG = "HttpUrlFetcher";

  private static final int MAXIMUM_REDIRECTS = 5;

  @VisibleForTesting static final String REDIRECT_HEADER_FIELD = "Location";



  @VisibleForTesting

  static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY =

      new DefaultHttpUrlConnectionFactory();

  /** Returned when a connection error prevented us from receiving an http error. */

 @VisibleForTesting static final int INVALID_STATUS_CODE = -1;



  private final GlideUrl glideUrl;

  private final int timeout;

  private final HttpUrlConnectionFactory connectionFactory;



  private HttpURLConnection urlConnection;

  private InputStream stream;

  private volatile boolean isCancelled;



  public HttpUrlFetcher(GlideUrl glideUrl, int timeout) {

    this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);

  }





  @Override

  public void loadData(

      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {

    long startTime = LogTime.getLogTime();

    try {

      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); 

 callback.onDataReady(result); 

    } catch (IOException e) {

      if (Log.isLoggable(TAG, Log.DEBUG)) {

        Log.d(TAG, "Failed to load data for url", e);

      }

      callback.onLoadFailed(e);

    } finally {

      if (Log.isLoggable(TAG, Log.VERBOSE)) {

        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));

      }

    }

  }



  private InputStream loadDataWithRedirects(

      URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException {

    if (redirects >= MAXIMUM_REDIRECTS) {

      throw new HttpException(

          "Too many (> " + MAXIMUM_REDIRECTS + ") redirects!", INVALID_STATUS_CODE);

    } else {

      // Comparing the URLs using .equals performs additional network I/O and is generally broken.

 // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.

 try {

        if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {

          throw new HttpException("In re-direct loop", INVALID_STATUS_CODE);

        }

      } catch (URISyntaxException e) {

        // Do nothing, this is best effort.

 }

    }



    urlConnection = buildAndConfigureConnection(url, headers);



    try {

      // Connect explicitly to avoid errors in decoders if connection fails.

 urlConnection.connect();

      // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.

 stream = urlConnection.getInputStream();

    } catch (IOException e) {

      throw new HttpException(

          "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e);

    }



    if (isCancelled) {

      return null;

    }



    final int statusCode = getHttpStatusCodeOrInvalid(urlConnection);

    if (isHttpOk(statusCode)) {

      return getStreamForSuccessfulRequest(urlConnection);

    } else if (isHttpRedirect(statusCode)) {

      String redirectUrlString = urlConnection.getHeaderField(REDIRECT_HEADER_FIELD);

      if (TextUtils.isEmpty(redirectUrlString)) {

        throw new HttpException("Received empty or null redirect url", statusCode);

      }

      URL redirectUrl;

      try {

        redirectUrl = new URL(url, redirectUrlString);

      } catch (MalformedURLException e) {

        throw new HttpException("Bad redirect url: " + redirectUrlString, statusCode, e);

      }

      // Closing the stream specifically is required to avoid leaking ResponseBodys in addition

 // to disconnecting the url connection below. See #2352.

 cleanup();

      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);

    } else if (statusCode == INVALID_STATUS_CODE) {

      throw new HttpException(statusCode);

    } else {

      try {

        throw new HttpException(urlConnection.getResponseMessage(), statusCode);

      } catch (IOException e) {

        throw new HttpException("Failed to get a response message", statusCode, e);

      }

    }

  }

}
3.3.3.7、从SourceGenerator的onDataReadyInternal中会回调到DecodeJob中的onDataFetcherReady中。

DecodeJob.java

@Override

public void onDataFetcherReady(

    Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {

  this.currentSourceKey = sourceKey;

  this.currentData = data;

  this.currentFetcher = fetcher;

  this.currentDataSource = dataSource;

  this.currentAttemptingKey = attemptedKey;

  this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);



  if (Thread.currentThread() != currentThread) {

    runReason = RunReason.DECODE_DATA;

    callback.reschedule(this);

  } else {

    GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");

    try {

      decodeFromRetrievedData(); 

    } finally {

      GlideTrace.endSection();

    }

  }

}





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, isLoadingFromAlternateCacheKey); 

  } else {

    runGenerators();

  }

}





private <Data> Resource<R> decodeFromData(

    DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {

  try {

    if (data == null) {

      return null;

    }

    long startTime = LogTime.getLogTime();

    Resource<R> result = decodeFromFetcher(data, dataSource); 

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      logWithTimeAndKey("Decoded result " + result, startTime);

    }

    return result;

  } finally {

    fetcher.cleanup();

  }

}



@SuppressWarnings("unchecked")

private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)

    throws GlideException {

  LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());

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

  DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);

  try {

    // ResourceType in DecodeCallback below is required for compilation to work with gradle.

 return path.load( 

 rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource)); 

  } finally {

    rewinder.cleanup();

  }

}

LoadPath.java

public Resource<Transcode> load(

    DataRewinder<Data> rewinder,

    @NonNull Options options,

    int width,

    int height,

    DecodePath.DecodeCallback<ResourceType> decodeCallback)

    throws GlideException {

  List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());

  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;

  //noinspection ForLoopReplaceableByForEach to improve perf

 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) {

      exceptions.add(e);

    }

    if (result != null) {

      break;

    }

  }



  if (result == null) {

    throw new GlideException(failureMessage, new ArrayList<>(exceptions));

  }



  return result;

}

DecodePath.java

public Resource<Transcode> decode(

    DataRewinder<DataType> rewinder,

    int width,

    int height,

    @NonNull Options options,

    DecodeCallback<ResourceType> callback)

    throws GlideException {

  Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);

  Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); 

  return transcoder.transcode(transformed, options);

}
3.3.3.8、将输入流转码为图片资源后,会通过回调的方式,将资源传递回DecodeJob中。
3.3.3.9、notifyEncodeAndRelease通知图片处理完成。

DecodeJob.java

private void notifyEncodeAndRelease(

    Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {

  if (resource instanceof Initializable) {

    ((Initializable) resource).initialize();

  }



  Resource<R> result = resource;

  LockedResource<R> lockedResource = null;

  if (deferredEncodeManager.hasResourceToEncode()) {

    lockedResource = LockedResource.obtain(resource);

    result = lockedResource;

  }



  notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey); 



  stage = Stage.ENCODE;

  try {

    if (deferredEncodeManager.hasResourceToEncode()) {

      deferredEncodeManager.encode(diskCacheProvider, options);

    }

  } finally {

    if (lockedResource != null) {

      lockedResource.unlock();

    }

  }

  // Call onEncodeComplete outside the finally block so that it's not called if the encode process

 // throws.

 onEncodeComplete();

}



private void notifyComplete(

    Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {

  setNotifiedOrThrow();

 callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey); 

}

EngineJob.java

@Override

public void onResourceReady(

    Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {

  synchronized (this) {

    this.resource = resource;

    this.dataSource = dataSource;

    this.isLoadedFromAlternateCacheKey = isLoadedFromAlternateCacheKey;

  }

  notifyCallbacksOfResult(); 

}







@Synthetic

void notifyCallbacksOfResult() {

  ResourceCallbacksAndExecutors copy;

  Key localKey;

  EngineResource<?> localResource;

  synchronized (this) {

    stateVerifier.throwIfRecycled();

    if (isCancelled) {

      // TODO: Seems like we might as well put this in the memory cache instead of just recycling

 // it since we've gotten this far...

 resource.recycle();

      release();

      return;

    } else if (cbs.isEmpty()) {

      throw new IllegalStateException("Received a resource without any callbacks to notify");

    } else if (hasResource) {

      throw new IllegalStateException("Already have resource");

    }

    engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);

    // Hold on to resource for duration of our callbacks below so we don't recycle it in the

 // middle of notifying if it synchronously released by one of the callbacks. Acquire it under

 // a lock here so that any newly added callback that executes before the next locked section

 // below can't recycle the resource before we call the callbacks.

 hasResource = true;

    copy = cbs.copy();

    incrementPendingCallbacks(copy.size() + 1);



    localKey = key;

    localResource = engineResource;

  }

  // 注释①

  engineJobListener.onEngineJobComplete(this, localKey, localResource); 



  for (final ResourceCallbackAndExecutor entry : copy) {

    //注释②

    entry.executor.execute(new CallResourceReady(entry.cb)); 

  }

  decrementPendingCallbacks();

}
3.3.3.10、注释①处,将新加载的图片添加到活动缓存。

Engine.java

@Override

public synchronized void onEngineJobComplete(

    EngineJob<?> engineJob, Key key, EngineResource<?> resource) {

  // A null resource indicates that the load failed, usually due to an exception.

 if (resource != null && resource.isMemoryCacheable()) {

    activeResources.activate(key, resource); 

  }



  jobs.removeIfCurrent(key, engineJob);

}
3.3.3.11、注释②处,将加载好的资源回调会EngineJob,进而通过cb.onResourceReady 回调回SingleRequest中的onResourceReady方法中,此处与前面从缓存中找到图片资源后的处理方式相同。

EngineJob.java

private class CallResourceReady implements Runnable {



  private final ResourceCallback cb;



  CallResourceReady(ResourceCallback cb) {

    this.cb = cb;

  }



  @Override

  public void run() {

    // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock

 // (b/136032534).

 synchronized (cb.getLock()) {

      synchronized (EngineJob.this) {

        if (cbs.contains(cb)) {

          // Acquire for this particular callback.

 engineResource.acquire();

          callCallbackOnResourceReady(cb); 

          removeCallback(cb);

        }

        decrementPendingCallbacks();

      }

    }

  }

}







@Synthetic

@GuardedBy("this")

void callCallbackOnResourceReady(ResourceCallback cb) {

  try {

    // This is overly broad, some Glide code is actually called here, but it's much

 // simpler to encapsulate here than to do so at the actual call point in the

 // Request implementation.

 cb.onResourceReady(engineResource, dataSource, isLoadedFromAlternateCacheKey); 

  } catch (Throwable t) {

    throw new CallbackException(t);

  }

}


\