Android圖片加載框架Glide源碼解析(二)

Glide的圖片加載流程

上一章節(jié)Android圖片加載框架Glide源碼解析(一)中講Glide的基本使用卵渴,對于不會使用的建議先去看下劲件。

體驗了Glide的便捷及強大后丑掺,我們一定很好奇Glide是如何加載圖片的或者說加載流程又是怎樣的呢谷浅?不用急秘血,本章我們就從源碼上一步步分析加載流程即彪。

1紧唱、Glide一次調(diào)用的代碼如下:

Glide.with(context)
    .load(url)
    .apply(RequestOptions.placeholderOf(R.drawable.ic_default)
                .error(R.drawable.ic_error))
    .thumbnail(0.4f)
    .into(imageview);

2、從調(diào)用流程入手進行分析

1.調(diào)用with()靜態(tài)方法

Glide類本身是一個單例隶校,調(diào)用靜態(tài)方法with()去實例化Glide類并返回RequestManager對象漏益,以供后面使用。靜態(tài)方法with()是一個重載方法惠况,參數(shù)可以是Context遭庶、Activity、Fragment稠屠、View峦睡、FragmentActivity這五種,源碼如下:

/**
 * 傳入對象是Context,
 */
@NonNull
public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}
//傳入對象是Activity
@NonNull
public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
}
//傳入對象是FragmentActivity
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
}
//傳入對象是Fragment
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}
//傳入對象是View
@NonNull
public static RequestManager with(@NonNull View view) {
  return getRetriever(view.getContext()).get(view);
}

從源碼可以看到权埠,這幾個重載方法內(nèi)部均調(diào)用了同一個靜態(tài)方法getRetriever()榨了,那么這個方法是干嘛用的呢,看源碼:

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

從源碼我們可以看到攘蔽,先去對參數(shù)context進行非空檢查龙屉,然后調(diào)用Glide的靜態(tài)方法get()采用懶漢式的方式實現(xiàn)單例,通過Builder模式實例化Glide對象满俗,由于篇幅這里就不貼初始化的代碼了转捕。初始化后調(diào)用getRequestManagerRetriever()得到RequestManagerRetriever對象,然后調(diào)用get()就能獲得一個RequestManager對象唆垃。

2.通過RequestManager類管理Request請求

RequestManager類是實現(xiàn)Glide加載資源的管理類五芝,根據(jù)Activity生命周期管控資源加載請求,可以開啟辕万、暫停及重啟枢步。
通過調(diào)用load()重載方法創(chuàng)建一個RequestBuilder沉删,去加載資源。

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

其中,asDrawable方法主要是設(shè)置加載的資源最終以Drawable對象返回醉途,默認情況下矾瑰,Glide加載圖片資源時以Drawable對象對象,目前Glide支持返回Drawable隘擎、Bitmap殴穴、gif和File四種,在不想使用默認的drawable返回的情況下可以在調(diào)用load前調(diào)用對應(yīng)的方法货葬,如Bitmap對應(yīng)asBitmap();

Glide.with(context)
      .asBitmap()    //該句表示加載資源后保存為Bitmap對象
      .load(url)
      .into(imageview);

注:如果調(diào)用asBitmap()后在調(diào)用load方法推正,此時調(diào)用的是RequestBuilder類的load方法,注意區(qū)分1Χ琛!再沧!
好了尼夺,回到正題,上述調(diào)用load后內(nèi)部是通過asDrawable()設(shè)置資源返回類型炒瘸,并返回一個RequestBuilder對象淤堵,然后調(diào)用RequestBuilder的方法load(),看一下源碼:

  public RequestBuilder<Drawable> asDrawable() {
      return as(Drawable.class);
  }

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

可以看到這里new了一個requestBuilder對象顷扩。

3.RequestOptions實現(xiàn)各種選項設(shè)置

通過上述返回requestBuilder對象后拐邪,后續(xù)的加載資源及一些設(shè)置就在requestBuilder類中實現(xiàn)了。在V4版本中隘截,Glide將一些選項設(shè)置模塊劃分出來扎阶,放在RequestOptions類中,其中包括設(shè)置占位圖婶芭、錯誤圖东臀、緩存機制、關(guān)閉動畫犀农、scaleType等惰赋。在調(diào)用apply()方法時傳入RequestOptions對象,apply()方法源碼如下:

//傳入RequestOptions類對象呵哨,并保存起來
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
}

//判斷默認選項和設(shè)置的是否一致,如果一致返回this.requestOptions的一個克隆赁濒,如果不一致則直接返回    this.requestOptions
protected RequestOptions getMutableOptions() {
    return defaultRequestOptions == this.requestOptions
        ? this.requestOptions.clone() : this.requestOptions;
  }

獲取到當(dāng)前的RequestOptions對象后,通過調(diào)用對象方法的apply方法孟害,將上傳入的requestOptions對象傳入并保持設(shè)置拒炎,方便后續(xù)使用,同時返回了新的RequestOptions對象賦值給this.requestOptions變量纹坐,下面是RequestOptions類中的apply()方法:

public RequestOptions apply(@NonNull RequestOptions other) {
//允許自動克隆枝冀,則直接克隆一個RequestOptions對象然后設(shè)置類各種屬性舞丛,并直接返回該對象
if (isAutoCloneEnabled) {
  return clone().apply(other);
}

  ...
 中間省略代碼主要用于判斷是否設(shè)置了對應(yīng)的屬性(占位圖、錯誤果漾、緩存等選項)球切,沒設(shè)置過就去設(shè)置,由于代碼量大绒障,這里不貼出了
  ...

// Applying options with dontTransform() is expected to clear our transformations.
//這里檢查是否要動畫效果吨凑,如果不需要,就清除所有過場動畫效果
if (!isTransformationAllowed) {
  transformations.clear();
  fields &= ~TRANSFORMATION;
  isTransformationRequired = false;
  fields &= ~TRANSFORMATION_REQUIRED;
  isScaleOnlyOrNoTransform = true;
}

fields |= other.fields;
options.putAll(other.options);

return selfOrThrowIfLocked();
}

Glide加載圖片資源中的一些選項設(shè)置都在這個RequestOptions類中户辱,該類也提供了一些選項設(shè)置的靜態(tài)方法鸵钝,設(shè)置完成直接返回requestOptions對象,不需要用戶自己去new庐镐。

4.調(diào)用RequestBuilder類中into()方法實現(xiàn)資源加載并設(shè)置到目標target中

前面幾點講述了資源加載之前的基礎(chǔ)設(shè)置及選項設(shè)置恩商,最后調(diào)用into實現(xiàn)資源的加載。into()方法時個重載類必逆,最終幾個方法都會進入到into(Y target,RequestListener<TranscodeType> targetListener,RequestOptions options)中怠堪,不廢話,直接看源碼:

private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @NonNull RequestOptions options) {
  Util.assertMainThread();//檢測是否在主線程名眉,非主線程調(diào)用時拋異常
  Preconditions.checkNotNull(target);//判斷target非空
  //防止在load之前調(diào)用into方法
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }

  options = options.autoClone();
  //通過buildRequest()方法構(gòu)建一個Request對象
  Request request = buildRequest(target, targetListener, options);
  //獲取target之前的request請求對象
  Request previous = target.getRequest();
  //將新構(gòu)建的request對象與舊的previous對象進行比較粟矿,判斷是否相同。
  // isSkipMemoryCacheWithCompletePreviousRequest用于判斷options是否有使用緩存機制和previous是否已經(jīng)加載完畢
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    request.recycle();
    //判斷previous是否正在運行损拢,如果沒在運行陌粹,則執(zhí)行previous這個請求
    if (!Preconditions.checkNotNull(previous).isRunning()) {
     previous.begin();
    }
    return target;
  }

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

  return target;
}

into方法中主要做了以下兩個方面的事情:

  • 通過buildRequest()方法構(gòu)建一個Request;

  • 獲取target的Request對象,與構(gòu)建的Request對象進行比較福压,并檢測是否采用緩存及target的上次請求是否已經(jīng)完成:
    1.兩個對象相等郑藏,并且options采用了緩存或target的上次請求未完成入偷,則將構(gòu)建的request進行回收歹叮,并且 判斷target的request是否正在運行包颁,正在運行則直接返回target,若為正在運行則調(diào)用Request的begin方法 啟動同步加載資源請求胞枕;
    2.兩個對象不等杆煞,或者option沒使用緩存機制和target的請求已經(jīng)完成,清除target之前的設(shè)置腐泻,并將新構(gòu)建的request對象設(shè)置給target决乎,通過track方法啟動Request請求;
    buildRequest()方法源碼:

    //構(gòu)建一個Request對象
    private Request buildRequest(Target<TranscodeType> target,
           @Nullable RequestListener<TranscodeType> targetListener,RequestOptions requestOptions) {
    return buildRequestRecursive(target,targetListener, /*parentCoordinator=*/ null,
      transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(),
      requestOptions.getOverrideHeight(), requestOptions);
    }
    
    /**
     * 主要做的事情就是先創(chuàng)建一個主加載資源的Request對象派桩,然后根據(jù)是否設(shè)置了加載錯誤時
     * 顯示資源選項构诚,創(chuàng)建一個errorRequest加載資源對象,最后返回Request
     **/
    private Request buildRequestRecursive(Target<TranscodeType> target,
    @Nullable RequestListener<TranscodeType> targetListener,
    @Nullable RequestCoordinator parentCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    RequestOptions requestOptions) {
    
    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
    //創(chuàng)建加載資源的Request請求
    Request mainRequest = buildThumbnailRequestRecursive( target,  targetListener,
            parentCoordinator, transitionOptions,  priority, overrideWidth, overrideHeight,
            requestOptions);
    
    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
    
    int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
    int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
    if (Util.isValidDimensions(overrideWidth, overrideHeight)
      && !errorBuilder.requestOptions.isValidOverride()) {
        errorOverrideWidth = requestOptions.getOverrideWidth();
        errorOverrideHeight = requestOptions.getOverrideHeight();
    }
    
    Request errorRequest = errorBuilder.buildRequestRecursive(
        target, targetListener,  errorRequestCoordinator, errorBuilder.transitionOptions,
      errorBuilder.requestOptions.getPriority(), errorOverrideWidth, errorOverrideHeight, errorBuilder.requestOptions);
          errorRequestCoordinator.setRequests(mainRequest, errorRequest);
      return errorRequestCoordinator;
    }
    

isSkipMemoryCacheWithCompletePreviousRequest方法源碼:

  private boolean isSkipMemoryCacheWithCompletePreviousRequest( RequestOptions options, Request previous) {
      return !options.isMemoryCacheable() && previous.isComplete();
}

當(dāng)條件不滿足時铆惑,先執(zhí)行clear范嘱,在執(zhí)行track方法送膳,實現(xiàn)Request請求,源碼如下:

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

該方法丑蛤,將target加入到targetTracker列表中叠聋,然后運行Request,看下runRequest方法:

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

代碼很簡單受裹,一看就懂碌补。
到目前為止一次Glide加載圖片資源的流程已經(jīng)完成大半了,后面就是看Request如何加載資源并顯示到target的棉饶。

5.Request實現(xiàn)加載資源及顯示

上面講到創(chuàng)建了Request對象厦章,但是Request對象是一個interface,那么Request是在哪里實現(xiàn)的呢照藻?追蹤上面創(chuàng)建的Request對象袜啃,很容易在RequestBuilder中發(fā)現(xiàn)obtainRequest方法,該方法如下:

private Request obtainRequest( Target<TranscodeType> target,
  RequestListener<TranscodeType> targetListener,RequestOptions requestOptions,
  RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,
  Priority priority,int overrideWidth,int overrideHeight) {
return SingleRequest.obtain(context,glideContext,model,transcodeClass,requestOptions,
    overrideWidth,overrideHeight,priority,target,
    targetListener, requestListeners, requestCoordinator, glideContext.getEngine(),
    transitionOptions.getTransitionFactory());
}

該方法通過SingleRequest類的方法obtain去創(chuàng)建一個Request對象幸缕,該類實現(xiàn)了Request囊骤、ResourceCallback、SizeReadyCallback等接口冀值,可以說這個類就是我們要找的。那么我們看下該類如何實現(xiàn)begin方法的宫屠,首先看下源碼:

@Override
public 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));
    }
  }

方法經(jīng)過一系列的條件判斷列疗,當(dāng)status是Running或者WAITING_FOR_SIZE狀態(tài),并且需要設(shè)置占位圖的時候浪蹂,會調(diào)用onLoadStarted設(shè)置占位圖抵栈,當(dāng)status是Status.COMPLETE時會執(zhí)行ResouceCallback接口的onResourceReady方法,繼續(xù)看代碼:

public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
  GlideException exception = new GlideException("Expected to receive a Resource<R> with an "
      + "object of " + transcodeClass + " inside, but instead got null.");
  //加載失敗
  onLoadFailed(exception);
  return;
}

Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
  releaseResource(resource);
  GlideException exception = new GlideException("Expected to receive an object of "
      + transcodeClass + " but instead" + " got "
      + (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
      + "Resource{" + resource + "}."
      + (received != null ? "" : " " + "To indicate failure return a null Resource "
      + "object, rather than a Resource object containing null data."));
  //加載失敗
  onLoadFailed(exception);
  return;
}
//判斷是否需要設(shè)置資源坤次,如果不需要設(shè)置則直接釋放資源并設(shè)置狀態(tài)為complete
if (!canSetResource()) {
  releaseResource(resource);
  // We can't put the status to complete before asking canSetResource().
  status = Status.COMPLETE;
  return;
}

onResourceReady((Resource<R>) resource, (R) received, dataSource);
}

方法主要對條件判斷古劲,對于不符合條件的直接跑出異常并調(diào)用onLoadFailed回調(diào)失敗結(jié)果,成功了后先判斷是否需要設(shè)置資源缰猴,不設(shè)置的話就吧資源釋放然后設(shè)置狀態(tài)為complete产艾,否則就調(diào)用onResourceReady()的重載方法進一步處理,

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);
  //設(shè)置資源及動畫相關(guān)信息
  if (!anyListenerHandledUpdatingTarget) {
    Transition<? super R> animation =
        animationFactory.build(dataSource, isFirstResource);
    target.onResourceReady(result, animation);
  }
} finally {
  isCallingCallbacks = false;
}
//最后通知加載成功
notifyLoadSuccess();
}

可以看到有這么一句:

target.onResourceReady(result, animation);

其實這里就是設(shè)置target資源,target分別與要設(shè)置資源的組件有關(guān)滑绒,比如Imageview闷堡,對應(yīng)ImageViewTarget,Glide中對應(yīng)如下幾個Target:


QQ截圖20180903095355.png

以ImageViewTarget類為例,onReourceReady()方法的實現(xiàn)如下:

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

protected abstract void setResource(@Nullable Z resource);

那么setResource()抽象方法是在哪里實現(xiàn)的呢疑故,這就要根據(jù)設(shè)置資源的返回類型決定了杠览,比如我們的返回類型是Drawable,則實現(xiàn)是在DrawableImageViewTarget類中纵势,實現(xiàn)如下:

/**
 * A target for display {@link Drawable} objects in {@link ImageView}s.
 */
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  ...

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

好了終于看到顯示資源的地方了踱阿。那么我們回到SingleRequest類的onResourceReady(Resource<R> resource, R result, DataSource dataSource)方法中管钳,最后又一句:

notifyLoadSuccess();

這個方法時做什么的呢,先看notifyLoadSuccess()的源碼:

private void notifyLoadSuccess() {
    if (requestCoordinator != null) {
      requestCoordinator.onRequestSuccess(this);
    }
}

方法很簡單软舌,就是判斷下requestCoordinator 非空才漆,然后執(zhí)行requestCoordinator對象方法onRequestSuccess(),那么requestCoordinator的實現(xiàn)在哪里呢葫隙,搜索很容易發(fā)現(xiàn)繼承RequestCoordinator接口的只有兩個類ThumbnailRequestCoordinator和ErrorRequestCoordinator栽烂,打開ThumbnailRequestCoordinator類,找到onRequestSuccess方法恋脚,可以看到:

@Override
 public void onRequestSuccess(Request request) {
    if (request.equals(thumb)) {
      return;
  }
  if (parent != null) {
    parent.onRequestSuccess(this);
  }
  // Clearing the thumb is not necessarily safe if the thumb is being displayed in the Target,
  // as a layer in a cross fade for example. The only way we know the thumb is not being
  // displayed and is therefore safe to clear is if the thumb request has not yet completed.
  if (!thumb.isComplete()) {
    thumb.clear();
  }
}

從源碼中看到這個方法主要做一些加載成功后的清理工作腺办。
由此可以看出,Glide加載成功后糟描,分別由Target和RequestCoordinator接管資源展示和request回收工作怀喉。

3、總結(jié)

源碼分析完了船响,我們總結(jié)出下面一個簡易的加載流程圖躬拢。


QQ截圖20180831171920.png

總結(jié)

  • 1.Glide類作為入口類,調(diào)用靜態(tài)方法with(),實例化了Glide這個單例见间,取得RequestManager對象并返回聊闯;
  • 2.RequestManager去管理Request請求,根據(jù)Activity或Fragment生命周期米诉,管理request請求菱蔬,包括:onStop,onStart,onDestory,pauseRequest,resumeRequests等,并可以設(shè)置加載資源要返回的資源類型史侣,Bitmap拴泌,Drawable,Gif。通過調(diào)用load()惊橱、asBitmap()蚪腐、asFile()等方法均返回一個RquestBuilder對象,將資源的請求處理移交給了RequestBuilder税朴,實現(xiàn)將功能分離回季,模塊化處理。
  • 3.RquestBuilder處理資源加載請求正林,調(diào)用apply()方法傳入RequestOptions對象進行一些選項設(shè)置;調(diào)用transition()方法實現(xiàn)資源加載顯示時過度動畫;最后調(diào)用into()方法茧跋,去構(gòu)建Rquest對象或使用Target設(shè)置過的Request對象,調(diào)用Rquest的對象方法begin()開啟加載請求卓囚。
  • 4.開啟請求后的工作就移交到Request類中瘾杭,加載完成后,Request調(diào)用onResourceReady()方法去判斷加載結(jié)果哪亿,若加載成功粥烁,調(diào)用Target類的onReadySuccess()方法設(shè)置資源贤笆,調(diào)用RequestCoordinator中方法onRequestSuccess()執(zhí)行加載成功后的資源清理工作。若加載失敗讨阻,Request直接調(diào)用notifyLoadFailed()方法芥永,將清理資源工作移交給RequestCoordinator類處理。



參考資源:
Glide源碼


Android圖片加載框架Glide源碼解析(一)
Android圖片加載框架Glide源碼解析(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钝吮,一起剝皮案震驚了整個濱河市埋涧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奇瘦,老刑警劉巖棘催,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耳标,居然都是意外死亡醇坝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門次坡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呼猪,“玉大人,你說我怎么就攤上這事砸琅∷尉啵” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵症脂,是天一觀的道長谚赎。 經(jīng)常有香客問我,道長摊腋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任嘁傀,我火速辦了婚禮兴蒸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘细办。我一直安慰自己橙凳,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布笑撞。 她就那樣靜靜地躺著岛啸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茴肥。 梳的紋絲不亂的頭發(fā)上坚踩,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機與錄音瓤狐,去河邊找鬼瞬铸。 笑死批幌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗓节。 我是一名探鬼主播荧缘,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拦宣!你這毒婦竟也來了截粗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤鸵隧,失蹤者是張志新(化名)和其女友劉穎绸罗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掰派,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡从诲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了靡羡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片系洛。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖略步,靈堂內(nèi)的尸體忽然破棺而出描扯,到底是詐尸還是另有隱情,我是刑警寧澤趟薄,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布绽诚,位于F島的核電站,受9級特大地震影響杭煎,放射性物質(zhì)發(fā)生泄漏恩够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一羡铲、第九天 我趴在偏房一處隱蔽的房頂上張望蜂桶。 院中可真熱鬧,春花似錦也切、人聲如沸扑媚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疆股。三九已至,卻和暖如春倒槐,著一層夾襖步出監(jiān)牢的瞬間旬痹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唱凯,地道東北人羡忘。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像磕昼,于是被迫代替她去往敵國和親卷雕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,747評論 2 361