Glide原理分析

glide調用方法比較簡單如下,這是一個最簡單的例子,後續流程分析就按照這個例子來:

Glide.with(this).load("http://u5.mm-img.mmarket.com/rs/res2/marketing_activities/1513240903060.png"

1.Glide的基本加載流程

1.1幾個重點類的解釋

Glide:glide圖片加載的入口類,裏面維護着它自己的唯一實例對象glide。

RequestManagerRetriever:這個類最主要的用處就是創建一個隱藏的RequestManagerFragment,用來感知宿主Activity的聲明週期變化,主要用來創建RequestManager並且保證對於同一個context來說只會創建一個RequestManager對象。

RequestManager:負責管理所有的Target對象額Request對象,並且實現了生命週期LifecycleListener接口,並且在構造方法中和宿主Activity的生命週期lifecycle進行了綁定,跟隨宿主生命週期變化而變化。其load方法會構建一個RequestBuilder對象,可以鏈式的設置參數供下一步使用。

RequestBuilder:內部包含了大量的請求參數,最主要的就是RequestOptions(請求參數相關)和TransitonOptions(加載完成動畫相關),其中into方法會創建Request對象和Target對象觸發加載請求。

RequestOptions:大量的請求參數,包括佔位符,緩存控制,加載完成之後的動畫等等。

Target:資源加載的目標接口,這個接口定義了資源加載的生命週期方法,比較常見的實現類是DrawableImageViewTarget,典型的生命週期流程:onLoadStarted -> onResourceReady或onLoadFailed -> onLoadCleared。

Request:Request負責真正的加載流程,一般Request和Target是一一對應的,典型的有SingleRequest。

LifecycleListener:定義了三個生命週期方法接口,onStart/onStop/onDestroy。

ModelTypes:定義了load方法的所有重載方式。

1.2基本加載流程

Glide#with:這一步最主要的就是獲取一個RequestManagerRetriever對象

public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
}

Glide#getRetriever:

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  // Context could be null for other reasons (ie the user passes in null), but in practice it will
  // only occur due to errors with the Fragment lifecycle.
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

Glide#get:構建一個Glide單例對象

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

  return glide;
}

Glide#getRequestManagerRetriever:這裏比較簡單,獲取RequestManagerRetriever對象,在Glide初始化時被賦值。

public RequestManagerRetriever getRequestManagerRetriever() {
  return requestManagerRetriever;
}

RequestManagerRetriever#get:RequestManagerRetriever#get方法重載比較多,選取其中一個分析即可。

public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

RequestManagerRetriever#fragmentGet:這個方法構建了一個RequestManagerFragment對象,它就是一個Fragment對象,裏面綁定了一些Lifecycle的方法,作用就是通過監聽它的生命週期變化而達到監聽到宿主Activity生命週期的目的。

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

RequestManagerRetriever#getRequestManagerFragment:這個方法比較常規

private RequestManagerFragment getRequestManagerFragment(
    @NonNull final android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart();
      }
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

GeneratedRequestManagerFactory#build:這個方法主要實現了RequestManager的構建過程。

final class GeneratedRequestManagerFactory implements RequestManagerRetriever.RequestManagerFactory {
  @Override
  @NonNull
  public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
      @NonNull RequestManagerTreeNode treeNode, @NonNull Context context) {
    return new GlideRequests(glide, lifecycle, treeNode, context);
  }
}

RequestManager#load:

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

RequestManager#as:

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

RequestBuilder#load:

public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
  return loadGeneric(bitmap)
      .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}

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

RequestBuilder#into:

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

  BaseRequestOptions<?> requestOptions = this;
  if (!requestOptions.isTransformationSet()
      && requestOptions.isTransformationAllowed()
      && view.getScaleType() != null) {
    // Clone in this method so that if we use this RequestBuilder to load into a View and then
    // into a different target, we don't retain the transformation applied based on the previous
    // View's scale type.
    switch (view.getScaleType()) {
      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());
}

public <X> ViewTarget<ImageView, X> buildImageViewTarget(
    @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
  return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}

public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
    @NonNull Class<Z> clazz) {
  if (Bitmap.class.equals(clazz)) {
    return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
  } else if (Drawable.class.isAssignableFrom(clazz)) {
    return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
  } else {
    throw new IllegalArgumentException(
        "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
  }
}

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  Preconditions.checkNotNull(target);
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }

  Request request = buildRequest(target, targetListener, options, callbackExecutor);

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

  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);

  return target;
}

RequestBuilder#buildRequest:先分析buildRequest源碼,下一步直接分析mainRequest的構建過程即可

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 buildRequestRecursive(
    Target<TranscodeType> target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @Nullable RequestCoordinator parentCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    BaseRequestOptions<?> requestOptions,
    Executor callbackExecutor) {

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

  Request mainRequest =
      buildThumbnailRequestRecursive(
          target,
          targetListener,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          requestOptions,
          callbackExecutor);

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

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

  Request errorRequest =
      errorBuilder.buildRequestRecursive(
          target,
          targetListener,
          errorRequestCoordinator,
          errorBuilder.transitionOptions,
          errorBuilder.getPriority(),
          errorOverrideWidth,
          errorOverrideHeight,
          errorBuilder,
          callbackExecutor);
  errorRequestCoordinator.setRequests(mainRequest, errorRequest);
  return errorRequestCoordinator;
}

RequestBuilder.buildThumbnailRequestRecursive:這個方法比較長,但是隻需要關注最後一個else的邏輯也就是obtainRequest方法即可

private Request buildThumbnailRequestRecursive(
    Target<TranscodeType> target,
    RequestListener<TranscodeType> targetListener,
    @Nullable RequestCoordinator parentCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    BaseRequestOptions<?> requestOptions,
    Executor callbackExecutor) {
  if (thumbnailBuilder != null) {
    // Recursive case: contains a potentially recursive thumbnail request builder.
    if (isThumbnailBuilt) {
      throw new IllegalStateException("You cannot use a request as both the main request and a "
          + "thumbnail, consider using clone() on the request(s) passed to thumbnail()");
    }

    TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
        thumbnailBuilder.transitionOptions;

    // Apply our transition by default to thumbnail requests but avoid overriding custom options
    // that may have been applied on the thumbnail request explicitly.
    if (thumbnailBuilder.isDefaultTransitionOptionsSet) {
      thumbTransitionOptions = transitionOptions;
    }

    Priority thumbPriority = thumbnailBuilder.isPrioritySet()
        ? thumbnailBuilder.getPriority() : getThumbnailPriority(priority);

    int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();
    int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();
    if (Util.isValidDimensions(overrideWidth, overrideHeight)
        && !thumbnailBuilder.isValidOverride()) {
      thumbOverrideWidth = requestOptions.getOverrideWidth();
      thumbOverrideHeight = requestOptions.getOverrideHeight();
    }

    ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
    Request fullRequest =
        obtainRequest(
            target,
            targetListener,
            requestOptions,
            coordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            callbackExecutor);
    isThumbnailBuilt = true;
    // Recursively generate thumbnail requests.
    Request thumbRequest =
        thumbnailBuilder.buildRequestRecursive(
            target,
            targetListener,
            coordinator,
            thumbTransitionOptions,
            thumbPriority,
            thumbOverrideWidth,
            thumbOverrideHeight,
            thumbnailBuilder,
            callbackExecutor);
    isThumbnailBuilt = false;
    coordinator.setRequests(fullRequest, thumbRequest);
    return coordinator;
  } else if (thumbSizeMultiplier != null) {
    // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
    ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
    Request fullRequest =
        obtainRequest(
            target,
            targetListener,
            requestOptions,
            coordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            callbackExecutor);
    BaseRequestOptions<?> thumbnailOptions =
        requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);

    Request thumbnailRequest =
        obtainRequest(
            target,
            targetListener,
            thumbnailOptions,
            coordinator,
            transitionOptions,
            getThumbnailPriority(priority),
            overrideWidth,
            overrideHeight,
            callbackExecutor);

    coordinator.setRequests(fullRequest, thumbnailRequest);
    return coordinator;
  } else {
    // Base case: no thumbnail.
    return obtainRequest(
        target,
        targetListener,
        requestOptions,
        parentCoordinator,
        transitionOptions,
        priority,
        overrideWidth,
        overrideHeight,
        callbackExecutor);
  }
}

RequestBuilder.obtainRequest:可以看到,先是從對象池裏面去取,有則共享,減少new對象的成本。然後調用init方法,進行一些參數設置

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

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

RequestBuilder.into:再回到RequestBuilder.into,這裏的target實現類是DrawableImageViewTarget,獲取此時是否有正在進行的Request請求,如果有,則進行邏輯判斷,決定是否需要開啓一個新的,還是複用之前的。顯然,我們這裏previous肯定是不存在的。因此需要將當前請求去執行,這裏RequestManager先是清除掉這個traget。

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  //省去buildRequest的代碼
  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);

  return target;
}

target.setRequest(request):將Request和View做了一個綁定的關係,保存在View的tag之中

public void setRequest(@Nullable Request request) {
  setTag(request);
}

private void setTag(@Nullable Object tag) {
  if (tagId == null) {
    isTagUsedAtLeastOnce = true;
    view.setTag(tag);
  } else {
    view.setTag(tagId, tag);
  }
}

RequestManager#track:TargetTracker和RequestTracker分別是對target和request做了一個管理,TargetTracker類中更加簡單,有點類似一個擴展的List結構,也就是保存了由當前RequestManager在處理的所有Target的集合,而RequestTracker則是所有Request的集合。

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

RequestTracker#runRequest:分爲兩種情況,isPaused變量標識界面是否處於onStop狀態,如果此時還可見,則直接調用request#begin方法執行,否則是加入到pendingRequests中,這裏pendingRequests的作用僅僅是爲了保證Request不被Gc,因爲requests是一個WeakHashMap,如果不使用pendingRequests強引用緩存,那麼這個請求就有可能被回收掉,這裏是做了這樣一個處理,就能保證這些request不被系統回收掉,同時在requests也一定存在。

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

Request#begin:就以SingleRequest#begin爲例,model此時就是我們傳的那個url,如果爲空,則直接load失敗,然後是一些狀態的檢查和一些回調方法等,接下來判斷size,如果是有效的,則觸發去真正的請求,否則則是設置一個回調,等待view佈局有size之後,再來觸發請求,真正的請求其實就在onSizeReady中被得到執行。

public synchronized void begin() {
  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);
    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));
  }
}

SingleRequest#onSizeReady:這個方法中,首先檢查狀態是不是在等待size,如果不是,則表明size已經有了,下面則是更新狀態到Status.RUNNING,進而去調用Engine根據參數,這裏麪包含了所有的參數信息,緩存,圖片顯示等等,然後去開始真正請求,網絡、內存、磁盤緩存等等。其實這裏面真正加載資源是在engine.load方法進行的,後面會做詳細分析。

public synchronized void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  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));
  }
}

Target#onLoadStarted:就以ImageViewTarget爲例來分析,這個方法的實現很簡單,就是爲view提前設置一些狀態,比如placeholder信息等等,然後等待Engine後續的加載完成。

public void onLoadStarted(@Nullable Drawable placeholder) {
  super.onLoadStarted(placeholder);
  setResourceInternal(null);
  setDrawable(placeholder);
}

2.Engine加載資源過程

2.1幾個關鍵類解釋

Key:一個接口,唯一標識加載資源數據的鍵值(包括原始數據和緩存數據)。

EngineKey:實現了key接口,內存緩存鍵值。

Resource:資源接口,常見的實現有DrawableResource、BitmapResource、NonOwnedBitmapResource、BytesResource、SimpleResource、EngineResource、LockedResource

MemoryCache:內存緩存接口,常見的實現類有LruResourceCache使用LRU的內存緩存。

DiskCache:磁盤緩存接口,常見的實現類有DiskLruCache基於LRU算法的磁盤緩存

DiskLruCacheWrapper:glide默認的磁盤緩存實現,其內部使用了DiskLruCache,基於LRU算法

ActiveResources:存放已經被request請求的資源,廣義上說是內存緩存的一種,其實是在內存緩存的基礎之上再做了一層緩存處理。

ResourceRecycler:回收Resource的輔助類

EngineJob:真正的進行資源加載並在加載完成時通知回調來管理加載的類

DecodeJob:從緩存數據或者原始數據解碼資源,並進行轉換的類

Jobs:緩存EngineJob的管理類,內部存放了key和engineJob的map對象。

2.2加載資源流程

2.2.1Engine#load

public synchronized <R> LoadStatus load(
          GlideContext glideContext,
          Object model,
          Key signature,
          int width,
          int height,
          Class<?> resourceClass,
          Class<R> transcodeClass,
          Priority priority,
          DiskCacheStrategy diskCacheStrategy,
          Map<Class<?>, Transformation<?>> transformations,
          boolean isTransformationRequired,
          boolean isScaleOnlyOrNoTransform,
          Options options,
          boolean isMemoryCacheable,
          boolean useUnlimitedSourceExecutorPool,
          boolean useAnimationPool,
          boolean onlyRetrieveFromCache,
          ResourceCallback cb,
          Executor callbackExecutor) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    //分析#1
    //構建一個engineey,下面對engineKey的構造輸入參數做下解析:
    //model:此時是圖片url
    //signature: 簽名?沒仔細的研究,一般情況下默認實現是EmptySignature。
    //width/height:加載的尺寸
    //transformations:是一個Map<Class<?>, Transformation<?>>鍵值對,常見的配對是:
    //Bitmap.class -> FitCenter
    //Drawable.class -> DrawableTransformation
    //BitmapDrawable.class -> DrawableTransformation
    //GifDrawable.class -> GifDrawableTransformation
    //resourceClass: 此時爲Object.class
    //transcodeClass: 指定了需要返回的Resource類型
    //options:Options類也是一個實現了Key的接口
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
            resourceClass, transcodeClass, options);
    //分析#2
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }
    //分析#3
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }

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

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

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

    jobs.put(key, engineJob);

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

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }

2.2.2Engine#loadFromActiveResources

private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
  if (!isMemoryCacheable) {
    return null;
  }
  EngineResource<?> active = activeResources.get(key);
  if (active != null) {
    active.acquire();
  }

  return active;
}

synchronized EngineResource<?> get(Key key) {
  ResourceWeakReference activeRef = activeEngineResources.get(key);
  if (activeRef == null) {
    return null;
  }

  EngineResource<?> active = activeRef.get();
  if (active == null) {
    cleanupActiveReference(activeRef);
  }
  return active;
}

這段代碼比較簡單,如果isMemoryCacheable可用就直接到activeResources中去查找,activeResources是怎麼保存的呢?activeEngineResources是一個Map<Key, ResourceWeakReference>對象。

2.2.3Engine#loadFromCache

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
  if (!isMemoryCacheable) {
    return null;
  }

  EngineResource<?> cached = getEngineResourceFromCache(key);
  if (cached != null) {
    cached.acquire();
    activeResources.activate(key, cached);
  }
  return cached;
}

很簡單只是對isMemoryCacheable的簡單判斷,接着看getEngineResourceFromCache

2.2.4Enging#getEngineResourceFromCache

private EngineResource<?> getEngineResourceFromCache(Key key) {
  Resource<?> cached = cache.remove(key);

  final EngineResource<?> result;
  if (cached == null) {
    result = null;
  } else if (cached instanceof EngineResource) {
    // Save an object allocation if we've cached an EngineResource (the typical case).
    result = (EngineResource<?>) cached;
  } else {
    result = new EngineResource<>(
        cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
  }
  return result;
}

cach的具體實現就是LruCache,內部維護了一個LinkedHashMap<Key, Resource<?> cache對象,很顯然如果是第一次加載的話這裏返回null。

2.2.5Enging#load

public <R> LoadStatus load(...{
    ...
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      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);
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }

接着繼續返回到load方法,如果cache返回null了就繼續在jobs中尋找是否存在一個EngineJob,如果存在就複用該EngineJob否則new一個執行start方法。

3.資源加載核心過程EngineJob和DecodeJob

3.1DecodeJob的執行過程

3.1.1 EngineJob#start

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

很簡單直接看DecodeJob#run方法。

3.1.2 DecodeJob#run

public void run() {
    // This should be much more fine grained, but since Java's thread pool implementation silently
    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
    // that something is failing.
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    // Methods in the try statement can invalidate currentFetcher, so set a local variable here to
    // ensure that the fetcher is cleaned up either way.
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (CallbackException e) {
      // If a callback not controlled by Glide throws an exception, we should avoid the Glide
      // specific debug logic below.
      throw e;
    } catch (Throwable t) {
      // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
      // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
      // are however ensuring that our callbacks are always notified when a load fails. Without this
      // notification, uncaught throwables never notify the corresponding callbacks, which can cause
      // loads to silently hang forever, a case that's especially bad for users using Futures on
      // background threads.
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "DecodeJob threw unexpectedly"
            + ", isCancelled: " + isCancelled
            + ", stage: " + stage, 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 {
      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
      // close in all cases anyway.
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  }

關鍵方法runWapped,執行邏輯如果出現異常jiuhui清理和重置DecodeJob裏面的一些資源。

3.1.3 DecodeJon#runWapped

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

private Stage getNextStage(Stage current) {
  switch (current) {
    case INITIALIZE:
      return diskCacheStrategy.decodeCachedResource()
          ? 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);
  }
}

runWrapped中主要是根據runReason獲取一個Stage狀態對象,第一次加載我們傳入的應該是INITIALIZE。接着看getNextStage的邏輯,diskCacheStrategy的值一般也是默認值DiskCacheStrategy.AUTOMATIC則decodeCachedResource()返回true,因此stage賦值爲Stage.RESOURCE_CACHE。接着看getNextGenerator的邏輯:

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

創建一個DataFetcherGenerator對象,getNextGenerator方法的實現很簡單,就是根據stage的信息,返回相應的對象,這裏我們的stage爲Stage.RESOURCE_CACHE,因此此時currentGenerator就是一個ResourceCacheGenerator對象。再接着,在runWrapped方法中,調用了runGenerators,繼續運行。

3.1.4 DecodeJob#runGenerators

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

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

這裏主要看while循環裏的邏輯,條件:!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())

1.檢查isCancelled狀態,一旦isCancelled爲true或者isStarted爲true,表明任務已經啓動,整個循環就會結束

2.stage狀態爲完成或者被取消並且任務沒有啓動,則直接notifyFailed

3.currentGenerator#startNext表示是否成功啓動了DataFetcher

DecodeJob的run方法,會依次從ResourceCacheGenerator->DataCacheGenerator->SourceGenerator這樣一個鏈執行,只要其中一個的startNext方法返回爲true,則不再尋找下一個Generator。下面重點看下startNex的邏輯,首先看ResourceCacheGenerator的startnext邏輯

3.1.5 ResourceCacheGenerator#startNext

public boolean startNext() {
    List<Key> sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) {
      return false;
    }
    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
    if (resourceClasses.isEmpty()) {
      if (File.class.equals(helper.getTranscodeClass())) {
        return false;
      }
      throw new IllegalStateException(
         "Failed to find any load path from " + helper.getModelClass() + " to "
             + helper.getTranscodeClass());
    }
    while (modelLoaders == null || !hasNextModelLoader()) {
      resourceClassIndex++;
      if (resourceClassIndex >= resourceClasses.size()) {
        sourceIdIndex++;
        if (sourceIdIndex >= sourceIds.size()) {
          return false;
        }
        resourceClassIndex = 0;
      }

      Key sourceId = sourceIds.get(sourceIdIndex);
      Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
      Transformation<?> transformation = helper.getTransformation(resourceClass);
      // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
      // we only run until the first one succeeds, the loop runs for only a limited
      // number of iterations on the order of 10-20 in the worst case.
      currentKey =
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData = modelLoader.buildLoadData(cacheFile,
          helper.getWidth(), helper.getHeight(), helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }

    return started;
  }

首先通過decodeHelper拿到的sourceIds就是[GlideUrl,ObjectKey],然後獲取resourceClass信息也就是glide所支持的資源類信息大致是[GifDrawable,Bitmap,BitmapDrawable]這些內容。下面是重點邏輯進入一個while循環:

1。由resourceClasses和sourceIds組成的一個正交關係,迭代每一組。

2.迭代開始前,若modelLoaders爲空或者,while內部先檢查是否已經全部迭代完成

3.對每一組,獲取相應的緩存Key對象,根據緩存key去diskcache中查找緩存文件,查找成功,則通過getModelLoaders獲取當前的modelLoaders信息,繼續執行循環。這個循環的主要工作就是找到modelLoaders信息,如果沒有找到startNext就直接返回false了交給下一個generator處理,找到的話就進入該方法的下一個while循環。這個循環邏輯比較簡單:遍歷modelLoaders,只要有一個對應的fetcher能夠處理,則startNext返回true,表明此時這個generator已經能夠處理本次請求,所以也不會再交給其他的generator對應的fetcher去處理了。

3.1.6 DataCacheGenerator#startNext

如果上一個generator沒有處理的話就進入DataCacheGenerator#startNext來了。

public boolean startNext() {
    while (modelLoaders == null || !hasNextModelLoader()) {
      sourceIdIndex++;
      if (sourceIdIndex >= cacheKeys.size()) {
        return false;
      }

      Key sourceId = cacheKeys.get(sourceIdIndex);
      // PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
      // and the actions it performs are much more expensive than a single allocation.
      @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey);
      if (cacheFile != null) {
        this.sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData =
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
              helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

DataCacheGenerator#startNext和ResourceCacheGenerator的startNext方法幾乎一模一樣,接着看SourceGenerator#startNext。

3.1.7 SourceGenerator#startNext

public boolean startNext() {
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }

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

  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    loadData = helper.getLoadData().get(loadDataListIndex++);
    if (loadData != null
        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
        || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
      started = true;
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

這個方法的主要邏輯還是while循環,最主要會調用MultFetcher的loadData方法去請求數據,可以看到loadData的第二個參數callback回調。失敗了層層回調也就結束了,成功會調用SourceGenerator#onDataReady。

3.1.8 SourceGenerator#onDataReady

public void onDataReady(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);
  }
}

這裏分爲兩部分:1.如果數據可以緩存的話cb.reschedule-會調用SourceGenerator的startNex緩存數據。2.如果不能緩存的話會回調到上一層處理,SourceGenerator的上一層爲DecodeJob,

3.2EngineJob與DecodeJob代碼詳細加載過程

glide加載過程就是由EngineJob觸發DecodeJob,DecodeJob中會有ResourceCacheGenerator->DataCacheGenerator->SourceGenerator對應的ModelLoaders與ModelFetchers依次處理,如果是SourceGenerator則還會更新緩存。在DecodeJob中獲取到數據之後,則會層層上報,由Fetcher->Generator->DecodeJob->EngineJob->SingleRequest->Target這樣一個序列回調

4.Glide的緩存架構

4.1內存緩存和磁盤緩存

4.1.1內存緩存

glide的內存緩存包括ActiveResources緩存和MemoryCache,MemoryCache我們很好理解,就是Resouce在內存中的緩存。主要說一下ActiveResources,ActiveResources緩存和MemoryCache是同時存在的。ActiveResources緩存存放的是所有未被clear的Request請求到的Resource,這部分Resource會存放至ActiveResources緩存中,當Request被clear的時候,會把這部分在ActiveResources緩存中的Resource移動至MemoryCache中去,只有MemoryCache中能夠命中,則這部分resource又會從MemoryCache移至ActiveResources緩存中去。其實相當於是對內存緩存再次做了一層,能夠有效的提高訪問速度。

4.1.2磁盤緩存

磁盤緩存比較簡單,其中也分爲ResourceCacheKey與DataCacheKey,一個是已經decode過的可以之間供Target給到View去渲染的,另一個是還未decode過的,緩存的是源數據。磁盤緩存的保存是在第一次請求網絡成功時候,會刷新磁盤緩存,此時處理的是源數據,至於是否會緩存decode過後的數據,取決於DiskCacheStrategy的策略。

5.Glide的自定義模塊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章