Android圖片載入庫Glide 知其然知其所以然 開篇

Drummor發表於2019-04-23

前言

我覺得使用第三方的庫時,要做到 知其然 知其所以然 欲然革之 棄之自然 好吧不裝x了說人話就是,對一個庫的瞭解程度可以化為為這樣幾個等級:

  • 首先,要能夠熟練使用它。
  • 然後,理解其中的原理,怎麼實現的,思考為什麼這樣實現
  • 再然後,在瞭解原理的基礎上,如果想要實現庫本身不支援的功能或者感覺實現的不好,這樣會情況下能夠改造它。
  • 最後,會用,原理懂了,能遊刃有餘的更改,還覺得自己的才華得不到施展,那就可以其之自然,也就是自己造輪子。 接下來,第一個階段就先跳過了,跳級進入第二個階段。拆輪子,探一探Glide這個庫時怎麼實現的。

整體Glide是怎樣架構的,大輪子如何轉動起來,通過這篇文章對Glide的整體有個認識然後逐一突破。 (這圖畫我了倆小時....,手動比心,求贊)

Android圖片載入庫Glide 知其然知其所以然 開篇

1、關於Glide的建立

Glide的簡單使用是下面這樣的:

Glide.with(context).load("http:xxxx").into(target) 
複製程式碼

先看看Glide.with()方法做了什麼。Glide的with(x)方法的引數可以是:Context、Activity、Fragment、View。 無論是哪種引數都會直接或者間接的呼叫下面這個方法:

private static RequestManagerRetriever getRetriever(@Nullable Context context){
    Preconditions.checkNotNull(context,"..");
    return Glide.get(context).getRequestManagerRetriever();
}
複製程式碼

這個方法最終會返回一個RequestManagerRetriever物件。Glide.get(ctx)返回的是一個Glide物件,內部使用的單例模式,程式內Glide是單例,如下。敲黑板,這不就是傳說中的雙重效驗鎖。

  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }
複製程式碼

找到了Glide建立的地方,Glide.get(ctx)->checkAndInitializeGlide(ctx)->initializeGlide(ctx,glideBuilder),最終建立glide單例物件的重擔落在了initializeGlide(..)方法上

  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    GeneratedAppGlideModule annotationGeneratedModulegetAnnotationGeneratedGlideModules();
    ....
    Glide glide = builder.build(applicationContext);
    ...
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }
複製程式碼

我們原始碼中關於使用APT技術動態載入GeneratedAppGlideModule物件然後配置Glide相關程式碼隱去了,篇幅有限這一部分單獨成篇詳細探究。

先看這個方法主要完成了三件事:

  • 使用APT技術動態載入GeneratedAppGlideModule物件然後配置Glide。(以後探其究竟)
  • 使用構造者模式GlideBuilder建立了了Glide物件
  • applicationContext.registerComponentCallbacks(glide),applicationContext註冊了ComponentCallbacks監聽,這裡傳入的上一步中新鮮建立的glide物件,glide實現了ComponentCallbacks2介面,介面中void onTrimMemory(@TrimMemoryLevel int level);的方法會在作業系統記憶體不足的時候會呼叫,我們能想到這是清理快取的好時機。

建立小結

image

# GlideBuild
 Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {...}
    if (diskCacheExecutor == null) {...}
    if (animationExecutor == null) {...}
    ...
    if (memoryCache == null) {...}
    if (diskCacheFactory == null) {...}
    if (engine == null) {...}
    return new Glide(context, engine,memoryCache,bitmapPool,arrayPool...)
  }
複製程式碼

如上圖經過層層最終呼叫initializeGlide(),這個方法中使用GlideBuilder物件夠建出Glide物件,值得關注的是他是一個單例。而且當在GlideBuider沒有設定響應引數的時候會生成預設的引數供GlideBuilder建立出Glide物件。

2、關於請求管理

2.1、RequestManagerRetriever

書接上回,我們已經對Glide這個類的建立有了一個大體的認知。

還是那行程式碼,

RequestManager requestManager = Glide.with(this);
複製程式碼

這行程式碼順序的做了三件事

  • 1、通過Glide glide = Glide.get(context)最終建立得到了一個Glide單例物件;
  • 2、呼叫glide.getRetriever()得到requestManagerRetriever物件
  • 3、RequestManager manager = requestManagerRetriever.getRequestManager(context)得到一個RequestManager的物件。

RequestManagerReriever類的註釋

A collection of static methods for creating new RequestManagers or retrieving existing ones from activities and fragment.

這個類的職責就是建立新的RequestManager或者在activity和fragment中檢索出已經存在的。

建立一個RequestManager的資料中很重要的一個就是LifeCycle

  • 有兩個類實現了LifeCycle分別是ActivityFragmentLifecycle和ApplicationLifecycle。主要區別就在於,ActivityFragmentLifecycle有感知Fragment/Activity宣告週期的能力。
  • 如果傳入的是Fragment或者Acitivy,requestManagerReriever檢視建立一個RequestManagerFragment物件,與其繫結,這樣就實現了對Activity,或者Fragment宣告週期的感知。
  • 如果傳入的是ApplicationContext就會傳入有兩個類實現了LifeCycle分別是ActivityFragmentLifecycle和ApplicationLifecycle。

摘了比較核心的一段程式碼標記,加了註釋。

  private RequestManager fragmentGet(@NonNull Context context,FragmentManager fm,Fragment parentHint,boolean isParentVisible) {
    //獲取一個RequestManagerFragment
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    //試圖在RequestManagerFragment中獲取requestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
    //如果為空就建立一個並且存到RequestManagerFragment中
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
複製程式碼

注意的點

  • RequestManagerReriever什麼時候建立的呢?在GlideBuilder中build方法中new了一個通過Glide的構造方法傳入的。
  • 當RequestManagerReriever.get(view)的時候,會查詢view所在的Activity進而去創建立Requestmanager,如果沒有找到所在Activity就使用ApplicationLifecycle構建Requestmanager。

總結下RequestManagerReriever物件建立RequestManager流程。

image

2.2 RequestManager、RequestBuilder

RequestManager見名知意,管理圖片請求,啟動、暫停、重新啟動請求,依據所在元件的生命週期週期和網路做出恰當的動作

使用RequestManager物件一系列的load()方法最終能夠得到一個RequestBuilder的物件, RequestBuilder是一個泛型類,載入什麼樣的型別圖片資源決定了他是什麼型別,圖片資源的來源可能是Recource、File、或者網路。

RequestBuilder<T> requestBuidlder = requestManager.load(xxxx);
複製程式碼

得到RequestBuilder之後,就可以對請求的圖片資源做一些期望設定

  • 比如你希望他是多大的,指定尺寸:override()。
  • 希望它使用什麼樣子的裁剪規則:centerCrop(),centerInside();..
  • 期望他的快取的機制是怎樣的:diskCacheStrategy()。

RequestBuildler,都可以通過RequestOptions進行配置。ReuqestBuilder繼承了BaseRequestOptions類,BaseRequestOption這個類是連線RequestOption和RequestBuilder的橋樑,通過這個類的物件對RequestOption進行一系列的設定。

Request有三個實現類,RequestCoordinator,SingleRequest和ErrorRequestCoordinator,RequestCoodinator。

  • RequestCoordinator中包含兩個Request,一個是 primary, error。RequestCoordinator使用primary發起請求,如果primary請求失敗了,就呼叫error請求。
  • SingleReuqest 實現了Request方法,載入來自不同型別的圖片資源。
  • ThumbnailRequestCoordinator 同時載入縮圖和原始圖。

#RequestBuilder 構建ErrorRequestCoordinator物件的方法為例。

  private Request buildRequestRecursive(..) {
    //構建請求控制
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
    //構建的主請求
    Request mainRequest =(...);
    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(..);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }
複製程式碼

整個Request構建管理的構成如下

Android圖片載入庫Glide 知其然知其所以然 開篇
這只是一個整體的流程,關於更詳細的Request管理,包括如果感知宣告週期的,重複請求如何管理,引數設定的原理等等單獨成篇分析。

3、Engine

Request物件的begin()方法根據圖片不同的來源去載入,獲取到期望尺寸之後就會呼叫onSizeReady(),傳入一系列引數資訊。

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

複製程式碼

engin.load()

  public synchronized <R> LoadStatus load(...) {
    ...
    EngineJob<R> engineJob =engineJobFactory.build(...);
    DecodeJob<R> decodeJob =decodeJobFactory.build(...);
    ...
    engineJob.addCallback(cb, callbackExecutor);//載入
    engineJob.start(decodeJob);//解碼,對二進位制資料轉換成圖片根據配置進行編輯
    ...
    return new LoadStatus(cb, engineJob);
  }
複製程式碼

EngineJob.load實際載入圖片資料的執行者,載入完之後交給decodeJob處理。內部具體如何實現的單獨成篇分析,如這部分中執行緒池如何使用,Bitmap的使用技巧等。

以上對Glide這個優秀的開源庫有了一個整體的認識,大體分為三個部分

  • Glide建立
  • Request管理
  • 載入解析 我們會單獨成篇嗎,還可能會把快取策略的應用縱向進行分析。最後再把我花了兩個小時的整體架構圖放上
    image

相關文章