圖片加載—Glide爲什麼這麼強?Glide源碼分析(上)

寫在前面

Github–Glide 鎮樓

源碼越看覺得東西越多,決定分兩篇來寫:


代碼實例

// how to use?
repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.11.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}



// For a simple view:
@Override public void onCreate(Bundle savedInstanceState) {
  ...
  ImageView imageView = (ImageView) findViewById(R.id.my_image_view);

  Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}

// For a simple image list:
@Override public View getView(int position, View recycled, ViewGroup container) {
  final ImageView myImageView;
  if (recycled == null) {
    myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
  } else {
    myImageView = (ImageView) recycled;
  }

  String url = myUrls.get(position);

  Glide
    .with(myFragment)
    .load(url)
    .centerCrop()
    .placeholder(R.drawable.loading_spinner)
    .into(myImageView);

  return myImageView;
}

上面是官方給出的例子,下面是我寫的例子hhh:

    // 夢開始的地方
    GlideApp.with(view.getContext())
         .load(url)
         .into(view);

Let’s begin!


GlideApp

Glide 4.x之前,未使用到GlideApp。4.x開始,Glide就不能調用placeHolder等函數了,而是需要通過GlideApp去調用才行。首先先看一下如何才能使用GlideApp:

GlideApp生成的方法:
①新建一個類繼承AppGlideModule
②類添加GlideModule
③Make Module

import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
      //可以配置Glide
}

爲什麼要使用GlideApp呢?Glide.with返回的是RequestManager對象,GlideApp.with返回的是GlideRequests,GlideRequests繼承了RequestManager。

GlideRequests中的函數只是調用了super.xxx(),即RequestManager中的實現;在load(url)調用後,返回的GlideRequest對象,而原來返回的是RequestManager。我們配置的許多參數,例如placeHolder、error、asBitmap等,都是保存RequestOptions中的,而RequestManager中的RequestOptions並沒有提供修改單一參數的方法。

下面我們來看一下GlideRequests的load:

  # GlideRequests
  
  @Override
  @NonNull
  @CheckResult
  public GlideRequest<Drawable> load(@Nullable String string) {
    // 調用RequestManager #load
    // GlideRequest<TranscodeType> 繼承 RequestBuilder<TranscodeType>
    return (GlideRequest<Drawable>) super.load(string);
  }
  
  
  # RequestManager
  
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    // 1.保存TranscodeType
    // 2.設置加載數據的Model
    return asDrawable().load(string);
  }

這時鏈式調用裏的對象就變成了GlideRequest,接下來我們來看一下placeHolder函數:

  # GlideRequest
  
  /**
   * @see GlideOptions#placeholder(Drawable)
   */
  @NonNull
  @CheckResult
  public GlideRequest<TranscodeType> placeholder(@Nullable Drawable drawable) {
    if (getMutableOptions() instanceof GlideOptions) {
      this.requestOptions = ((GlideOptions) getMutableOptions()).placeholder(drawable);
    } else {
      this.requestOptions = new GlideOptions().apply(this.requestOptions).placeholder(drawable);
    }
    return this;
  }

GlideRequest的placeHolder都是調用了GlideOptions的placeHolder。GlideOptions繼承RequestOptions:

  # GlideOptions
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions apply(@NonNull RequestOptions options) {
    return (GlideOptions) super.apply(options);
  }
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions placeholder(@Nullable Drawable drawable) {
    return (GlideOptions) super.placeholder(drawable);
  }

總結一下:GlideApp返回的GlideRequests、GlideRequest實現了GlideOptions(RequestOptions)中單一參數的修改方法,鏈式調用更加方便,建議使用。


with(Context)

作爲Glide的入口,先調用with函數傳入Context,我們來看看with的代碼:

  # GlideApp
  
  /**
   * @see Glide#with(Context)
   */
  @NonNull
  public static GlideRequests with(@NonNull Context context) {
    return (GlideRequests) Glide.with(context);
  }
  
  
  # Glide
  
  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
  
  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
  
  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }
  
  @NonNull
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
  
  @NonNull
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

Glide提供了這些重載的方法方便了初始化操作。使用Context啓動的任何請求將僅應用Application級別的選項,並且不會基於生命週期事件啓動或停止。

在使用Glide加載圖片資源時,圖片資源的請求、加載應該跟隨着Target的生命週期去走,即:如果ImageView在Activity中,我們應該在with中傳入Activity對象;如果在Fragment中,那麼我們應該傳入Fragment對象,等等。這樣,Glide才能正確的處理生命週期。

我們可以看到所有的重載都先調用了getRetriever:

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    ··· // check not null
    return Glide.get(context).getRequestManagerRetriever();
  }
  
  // Glide.get(context)就是一個單例模式創建Glide對象,最後會調用到initializeGlide
  
  @SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    // get app context
    Context applicationContext = context.getApplicationContext();
    
    // 找到@GlideModule註解生成的實現類‘com.bumptech.glide.GeneratedAppGlideModuleImpl’
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }

    ···

    // 將AppGlideModule中配置的各種參數賦值到GlideBuilder中
    // 1.RequestManagerFactory
    // 2.Options

    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    
    // 生成Glide
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    
    // 監聽app生命週期
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }
  
  
  # RequestManagerRetriever
  
  // 根據Context類型調用不同的get
  @NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      // 必須在UI線程
      
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }
  
  // 我們以FragmentActivity爲例
  @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      // 不在UI線程,改爲AppContext
      return get(activity.getApplicationContext());
    } else {
      // activity不能被destroy
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
  
  @NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    
    // 註釋1:獲取SupportRequestManagerFragment,關於該類的分析請往下看
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
        
    // 爲其設置RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      
      // 創建RequestManager
      // 註釋2:獲取SupportRequestManagerFragment中的lifecycle,詳情見下文
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
 
註釋1

SupportRequestManagerFragment是幹什麼的呢?我們想象這樣一個場景:你需要加載一張圖片到activity,但是網絡速度很慢。在圖片加載完成之前,你關閉了activity,那麼這時圖片還應該繼續加載到Target嗎?顯然是不應該。但是Glide本身無法感知組件的生命週期,所以就創建了一個無界面的fragment,即SupportRequestManagerFragment:

  @VisibleForTesting
  @SuppressLint("ValidFragment")
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
  
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }
註釋2

RequestManager的初始化傳入了lifecycle:

  # RequestManager
  
  // RequestManager本身也實現了LifecycleListener
  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      ···) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    ···

    // If we're the application level request manager, we may be created on a background thread.
    // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
    // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
    // This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);

    ···
  }
 
  
  @Override
  public void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  
  @Override
  public void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }
  
  public void pauseRequests() {
    Util.assertMainThread();
    requestTracker.pauseRequests();
  }
  
  
  # RequestTracker
  
  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      if (request.isRunning()) {
        request.clear();
        pendingRequests.add(request);
      }
    }
  }

這樣,當生命週期發生變化時,SupportRequestManagerFragment感知到變化並通知RequestManager,RequestManager通過RequestTracker來通知當前界面的Request,來做出對應的反應。


load

Glide能夠加載的圖片類型、來源也是多種多樣,所有load也重載了很多方法。我們以最常見的String爲例:

  # GlideRequests
  
  @Override
  @NonNull
  @CheckResult
  public GlideRequest<Drawable> load(@Nullable String string) {
    return (GlideRequest<Drawable>) super.load(string);
  }
  
  
  # RequestManager
  
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
  
  
  // 1.asDrawable
  // 嘗試始終使用任何可解碼{@link Drawable}的任何子類的已註冊{@link ResourceDecoder}加載資源。
  // 默認情況下,可以返回BitmapDrawable或GifDrawable,
  // 但如果爲其他{@link Drawable}子類註冊了其他解碼器,則也可能會返回這些子類中的任何一個。
  // 2.load
  @NonNull
  @Override
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    // 保存model
    this.model = model;
    
    // 調用into時會檢查這個標誌位
    isModelSet = true;
    return this;
  }

load調用完,鏈式中的對象就變成了GlideRequest,在這裏簡單介紹一下:

該類包含RequestBuilder中的所有public方法,RequestOptions中的所有options,以及GlideExtension註解的類中所有帶註解的方法。它的方法主要就是調用RequestOptions的方法來爲參數賦值,這裏我們舉一個例子來看一下:

GlideRequest

  /**
   * @see GlideOptions#diskCacheStrategy(DiskCacheStrategy)
   */
  @NonNull
  @CheckResult
  public GlideRequest<TranscodeType> diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    if (getMutableOptions() instanceof GlideOptions) {
      this.requestOptions = ((GlideOptions) getMutableOptions()).diskCacheStrategy(strategy);
    } else {
      this.requestOptions = new GlideOptions().apply(this.requestOptions).diskCacheStrategy(strategy);
    }
    return this;
  }
  
  
  # GlideOptions
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    return (GlideOptions) super.diskCacheStrategy(strategy);
  }
  
  
  # RequestOptions
  
  /**
   * Sets the {@link DiskCacheStrategy} to use for this load.
   *
   * <p> Defaults to {@link DiskCacheStrategy#AUTOMATIC}. </p>
   *
   * 大多數應用程序使用{@ link DiskCacheStrategy#RESOURCE}
   * 多次使用相同資源且具有多種大小,並希望在某些速度和磁盤空間之間進行權衡以換取較低帶寬使用量的應用程序,
   * 可能需要考慮使用{@link DiskCacheStrategy#DATA}或{@link DiskCacheStrategy#ALL}。 </ p>
   */
  @NonNull
  @CheckResult
  public RequestOptions diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    if (isAutoCloneEnabled) {
      return clone().diskCacheStrategy(strategy);
    }
    this.diskCacheStrategy = Preconditions.checkNotNull(strategy);
    
    // 標記該參數
    fields |= DISK_CACHE_STRATEGY;

    return selfOrThrowIfLocked();
  }

field也是一個經典用法,用於標記各個參數是否被主動賦值。各個參數的標記常量爲:

field

即通過二進制位來標記所有參數。


into

首先我們來看一下into的流程:

  # RequestBuilder
  
  // 設置資源將被加載到的{@link ImageView}
  // 取消View中所有現有的加載,並釋放Glide先前可能已加載到View中的任何資源,以便可以複用
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    // UI線程
    Util.assertMainThread();
    // view不爲null
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    
    // 如果設置了TransForm
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      
      // 不直接修改RequestOptions,而是clone出一個新的
      // 這樣,複用時不會受到view的scaleType影響
      switch (view.getScaleType()) {
        case CENTER_CROP:
          // center crop
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          // center inside
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          // fit center
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          // center inside
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    // 1.構建ViewTarget
    // 2.調用重載方法into
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }
  
  
  // 1
  # GlideContext
  
  @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
  
  
  # 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)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
  
  
  // 2
  # RequestBuilder
  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    // UI線程
    Util.assertMainThread();
    // target不爲null
    Preconditions.checkNotNull(target);
    
    // 在load時講過的,判斷是否設置model的標記位
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    // build request
    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);

    // 與之前的request進行對比
    
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // 如果請求完成,則重新開始將確保結果重新傳遞,從而觸發RequestListeners和Targets
      // 如果請求失敗,則重新開始將重新啓動請求,使它有另一個完成的機會
      // 如果請求已經在運行,我們可以讓它繼續運行而不會中斷
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // 使用上一個請求而不是新請求,達到優化的目的
        // 例如跳過設置佔位符,跟蹤和取消跟蹤目標以及獲取在單個請求中完成的視圖維度
        previous.begin();
      }
      return target;
    }

    // 跟蹤和取消跟蹤目標,保存request
    requestManager.clear(target);
    target.setRequest(request);
    
    // 發起請求
    requestManager.track(target, request);

    return target;
  }
  
  
  # RequestManager
  
  void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  
  
  # RequestTracker
  
  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);
    }
  }

總結一下:先根據傳入的View來創建ViewTarget,再根據options來創建Request,最後發起請求。而請求資源的過程就交給了RequestTracker,過程如何呢?又用了什麼巧妙的方式來設計的呢?這篇已經太長了,我要分p了。


如果文章中有錯誤的地方,希望各位大佬們批評指正~

If you like this article, it is written by Johnny Deng.
If not, I don’t know who wrote it.

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