Android Glide源碼剖析系列(二)Glide如何管理圖片加載請(qǐng)求


Glide源碼剖析系列


為什么選擇Glide?

  • 多種圖片格式的緩存,適用于更多的內(nèi)容表現(xiàn)形式(如Gif、WebP、縮略圖、Video)
  • 生命周期集成(根據(jù)Activity或者Fragment的生命周期管理圖片加載請(qǐng)求)Glide可以感知調(diào)用頁(yè)面的生命周期,這就是優(yōu)勢(shì)
  • 高效處理Bitmap(bitmap的復(fù)用和主動(dòng)回收,減少系統(tǒng)回收壓力)
  • 高效的緩存策略,靈活(Picasso只會(huì)緩存原始尺寸的圖片,Glide緩存的是多種規(guī)格),加載速度快且內(nèi)存開(kāi)銷小(默認(rèn)Bitmap格式的不同,使得內(nèi)存開(kāi)銷是Picasso的一半)

小結(jié):支持圖片格式多;Bitmap復(fù)用和主動(dòng)回收;生命周期感應(yīng);優(yōu)秀的緩存策略;加載速度快(Bitmap默認(rèn)格式RGB565)

Glide簡(jiǎn)單使用

Glide.with(this)
        .load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
        .into(imageView);

源碼分析

通過(guò)Android Glide源碼解析系列(一)圖片加載請(qǐng)求如何感知組件生命周期一文我們知道,with()方法返回的是一個(gè)RequestManager對(duì)象,說(shuō)明load()方法是在RequestManager類當(dāng)中的,所以我們首先要看的就是RequestManager這個(gè)類。

load()重載方法

load()方法支持多種形式的圖片來(lái)源,本文以RequestManager類中的 load(String string) 方法為例進(jìn)行深入分析。

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

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

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

為了使邏輯更清晰,把上面三個(gè)方法合并成一行代碼來(lái)分析

RequestManager#load(String string)
//相當(dāng)于
new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);

load(String string)最終被分為兩步執(zhí)行:

  1. Drawable.class為參數(shù)創(chuàng)建RequestBuilder實(shí)例
    RequestBuilder構(gòu)造函數(shù)
  protected RequestBuilder(
      @NonNull Glide glide,
      RequestManager requestManager,
      Class<TranscodeType> transcodeClass,
      Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;  //重要變量賦值為Drawable.class,構(gòu)建ImageViewTarget的時(shí)候使用
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();

    initRequestListeners(requestManager.getDefaultRequestListeners());    //初始化默認(rèn)請(qǐng)求監(jiān)聽(tīng)
    apply(requestManager.getDefaultRequestOptions());    //應(yīng)用默認(rèn)配置信息
  }
  1. 調(diào)用RequestBuilder#load(String string)
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    if (isAutoCloneEnabled()) {
      return clone().loadGeneric(model);
    }
    this.model = model;  //String變量賦值給model
    isModelSet = true;
    return selfOrThrowIfLocked();
  }

RequestBuilder重要成員變量:

  • transcodeClas = Drawable.class, 構(gòu)建ImageViewTarget的時(shí)候使用
  • model = 圖片來(lái)源為String地址,構(gòu)建Request的時(shí)候使用

RequestManager#load(String string)分析到現(xiàn)在,似乎都是一些準(zhǔn)備工作,真正的圖片加載還沒(méi)有出現(xiàn)。既然“千呼萬(wàn)喚不出來(lái)”,那咱們就“打破砂鍋查到底”,繼續(xù)查看RequestBuilder#into(ImageView view)方法

#RequestBuilder.java
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();  //判斷是否在主線程執(zhí)行
    Preconditions.checkNotNull(view);

    //把ImageView的scaleType寫進(jìn)requestOptions 
    //省略代碼

    return into(
        //注釋1:構(gòu)建新的ImageViewTarget
        glideContext.buildImageViewTarget(view, transcodeClass),  
        /*targetListener=*/ null,
        requestOptions,  //請(qǐng)求選項(xiàng)
        Executors.mainThreadExecutor());  //主線程池,切回到主線程顯示圖片
  }

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

    //注釋2:構(gòu)建新的加載請(qǐng)求Request 
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();  //獲取目標(biāo)View上已經(jīng)存在的舊請(qǐng)求
    if (request.isEquivalentTo(previous)  //兩次的請(qǐng)求相同 && !(跳過(guò)內(nèi)存緩存 && 舊請(qǐng)求已完成)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
      if (!Preconditions.checkNotNull(previous).isRunning()) {  //如果舊請(qǐng)求未開(kāi)始
        previous.begin();  //啟動(dòng)舊請(qǐng)求
      }
      return target;
    }
  
    //取消Glide為target準(zhǔn)備的所有加載請(qǐng)求,并釋放已經(jīng)
    //加載的資源(例如Bitmap),以便它們可以被重用。
    requestManager.clear(target); 

    //利用View.setTag把request請(qǐng)求綁定到target指向的View
    target.setRequest(request); 

    //注釋3
    requestManager.track(target, request);

    return target;
  }

  private boolean isSkipMemoryCacheWithCompletePreviousRequest(
      BaseRequestOptions<?> options, Request previous) {
    return !options.isMemoryCacheable() && previous.isComplete();
  }

小結(jié):

  1. 調(diào)用方法glideContext.buildImageViewTarget(view, transcodeClass)創(chuàng)建新的ImageViewTarget
  2. 創(chuàng)建新的加載請(qǐng)求request,并且檢查target上是否已有老請(qǐng)求previous:
    2.1 如果兩次的請(qǐng)求相同 && !(跳過(guò)內(nèi)存緩存 && 舊請(qǐng)求已完成),into()方法直接返回target
    2.2 如果滿足2.1且 && 請(qǐng)求未開(kāi)始,先啟動(dòng)舊請(qǐng)求再返回target
    2.3 如果2.1和2.2都不滿足,依次執(zhí)行:取消Glide為target準(zhǔn)備的所有加載請(qǐng)求,并釋放已經(jīng)加載的資源(例如Bitmap),以便它們可以被重用;利用View.setTag把新請(qǐng)求綁定到target指向的View;requestManager重新管理target和request。

代碼注釋1分析:

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

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  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)) {  //transcodeClas = Drawable.class
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}

根據(jù)transcodeClass類型創(chuàng)建對(duì)應(yīng)的ImageViewTarget,即Drawable.class -> DrawableImageViewTarget

代碼注釋2分析:Request創(chuàng)建過(guò)程比較復(fù)雜,還要處理縮略圖加載請(qǐng)求、加載錯(cuò)誤圖片加載請(qǐng)求等等,因此我們只分析主線buildRequest -> buildRequestRecursive -> buildThumbnailRequestRecursive -> obtainRequest -> SingleRequest.obtain -> new SingleRequest()。最終創(chuàng)建出一個(gè)SingleRequest實(shí)例

代碼注釋3分析:

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

主要做了兩件事:

  1. target添加到targetTracker,管理target指向的View生命周期回調(diào)事件
  2. request添加到requestTracker,管理圖片加載請(qǐng)求

targetTracker和requestTracker都是RequestManager的成員變量,分別負(fù)責(zé)管理target和request

TargetTracker源碼:

/**
 * TargetTracker 職責(zé)是維護(hù)一個(gè)Target列表,統(tǒng)一管理所有Target的onStart()、
 * onStop()和onDestroy()方法回調(diào)事件
 */
public final class TargetTracker implements LifecycleListener {
  //維護(hù)target列表
  private final Set<Target<?>> targets =
      Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());

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

RequestTracker部分源碼:

public class RequestTracker {
  private static final String TAG = "RequestTracker";
  // 如果Set直接持有request強(qiáng)引用,可能會(huì)發(fā)生內(nèi)存泄漏,因此將request作為key存儲(chǔ)在WeakHashMap里 
  //面,如果這些key不再使用,WeakHashMap會(huì)自動(dòng)異常這些key,從而避免內(nèi)存泄漏。
  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());

  //直接持有未完成和等待開(kāi)始的request強(qiáng)引用對(duì)象,防止在運(yùn)行過(guò)程中被GC
  private final Set<Request> pendingRequests = new HashSet<>();

//所有請(qǐng)求是否已經(jīng)被暫停
  private boolean isPaused;

  /** Starts tracking the given request. 把請(qǐng)求添加到requests和pendingRequests*/
  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);
    }
  }

  /** Stops any in progress requests. 停止正在運(yùn)行的請(qǐng)求*/
  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);
      }
    }
  }

  /** Starts any not yet completed or failed requests. 開(kāi)啟未完成或失敗的請(qǐng)求*/
  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();
  }
  
  //省略其他方法
}

圖片加載請(qǐng)求的管理流程END

結(jié)語(yǔ)

Glide管理圖片加載請(qǐng)求的方式并不復(fù)雜:使用一個(gè)請(qǐng)求管理類RequestTracker 維護(hù)所有請(qǐng)求列表,并且提供統(tǒng)一操作所有請(qǐng)求的方法(即所有請(qǐng)求的開(kāi)啟、暫停和重啟等等方法),RequestManager只要通過(guò)一個(gè)RequestTracker 對(duì)象就能輕輕松松控制圖片加載請(qǐng)求。

下一篇文章我們將會(huì)學(xué)習(xí)Glide如何啟動(dòng)圖片加載請(qǐng)求,以及圖片資源是如何經(jīng)歷重重改造最終顯示到界面上,敬請(qǐng)期待!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容