淺談 Fresco 框架結構

huansky發表於2021-04-18

在前面的文章 Fresco 原始碼分析 —— 圖片載入流程 裡面詳細說明了圖片載入的整個流程,但是除了理解原始碼之外,對於原始碼的框架層面的設計也是需要去了解的,不能只是簡單的讀原始碼,好的原始碼的框架設計也是值得我們去學習的。以後,我們自己在開發一個原始碼的時候,也就能將學到的好的經驗運用到自己的程式碼上。

程式碼工程

從 module 層面來看,可以看到 Fresco 是有很多 module 的,並且這些 module 都是按照每個 module 的功能進行劃分的,可以通過名字就可以知道 module 的作用。

雖然 module 很多,但是並不像我們平時的專案裡面一樣,一個 module 包含很多程式碼,有的可能只有幾個類,只是 fresco 整體分的比較細。

下面介紹下一些關鍵的 module:

  • animated-base:主要都是動圖的一些共有的基礎操作,包闊每一幀的快取,解碼渲染,幀數,寬高等邏輯。

  • Drawee:就是 UI 層,例如 DraweeView, drawable 相關的類等;

  • imagepipeline:整個工程的核心,圖片載入,記憶體、快取管理,bitmap處理等等核心邏輯;

  • imagepipeline-base:是 imagepipeline 的一些基礎類,包括介面(快取,解碼,圖片資訊)以及相關的基礎類;

  • imagepipeline-backends :這裡就是最後的請求邏輯,這裡給出兩個不同的示例,分別採用 volly 和 ok-http 來實現的,預設是 HttpUrlConnectionNetworkFetcher ,也就是說業務方有著更復雜的業務需求的話,需要自己去實現。

  • Drawee-backends:主要是在 Drawee 的基礎上封裝了請求和初始化邏輯,比如 Fresco,PipelineDraweeController 等相關類。

 

設計思想淺談 

1、裡面有很多類以 config 結尾,他們是怎麼用的?

ImagePipelineConfig:內部採用 builder 模式來進行建立,主要是把 ImagePipeline 需要的引數都通過 config 來進行管理;

ImagePipelineFactory: 通過 ImagePipelineConfig 來生成 ImagePipeline ;

類似的還有:

  • DraweeConfig: 內部採用 builder 模式來進行建立,包含 Drawee 相關的配置;

  • DiskCacheConfig:內部採用 builder 模式來進行建立,包含 disk 的各種配置,包括目錄,版本,快取大小,錯誤日誌等。最後也是使用 DiskStorageCacheFactory 來生成 disk;

  • PoolConfig:內部採用 builder 模式來進行建立,其實最終也是再 PoolFactory 裡面使用;

可以發現,Fresco 裡面的 config 類都是採用了 buidler 模式。那為啥需要採用 builder 模式呢?因為 config 從名字來說是配置類,裡面會有很多引數,所以會採用 builder 模式,以後別人在配置的時候,就不會關心過的引數問題。

 

其實對於內部 builder 我有個疑問就是為啥要使用內部 builder 。 

 

  • 一是內部屬性太多,如果採用建構函式模式,那需要寫很多建構函式

  • 二是採用builder 模式後,使用者只需要設定他關心的屬性,其他不關心的屬性都可以採用預設值來進行處理,也就是減輕了使用者的壓力。

  • 三是一旦構造完成,就不可以修改了,builder 裡面都是設定屬性,但是類本身只提供獲取屬性方法,不提供設定方法,隔絕了使用者更改帶來的不可控因素。

  • 至於內部builder 可能是不希望將他們獨立出去,散落在各處不好管理。

 

 

2、提供了很多 producer,consumer,那這麼多類是如何管理的,他們之間惡關係如何維護。

提供了 ProducerFactory 來管理所有的 producer。ProducerFactory 有靜態方法,大多數是非靜態方法。主要用來獲取各種 producer 。

ProducerSequenceFactory: 這個其實就是把各個 Producer 連線在一起;或者說是按照一定的規則,將他們組裝在一起。這樣外界在呼叫的時候,只需要確定你的 Sequence 是什麼樣的,呼叫對應的方法 獲取 Sequence。其中在 Sequence 裡面又會通過 ProducerFactory 來獲取指定的

每一個 producer 又會有一個對應的 consumer,可以發現大多數 consumer 都是 producer 裡面的內部類。

 

3、DataSource 的作用

DataSource 是一個泛型介面。按照原始碼的描述,它和 future 原理差不多,但是有個不一樣的地方,就是它可以獲取當前的進度。

AbstractDataSource 繼承自 DataSource;這塊內部已經維護好了各種狀態;然後會通過 listeners 進行通知。

AbstractProducerToDataSourceAdapter : 繼承自 AbstractDataSource,從名字就可以看出來這是一個介面卡,將 Producer 轉為 DataSource。

 

4、ProducerContext 的作用

主要是用來將上下文資訊傳遞給 Producer;可以具體看看程式碼,可以發現 context 內部包含很多邏輯。

public interface ProducerContext {

  @StringDef({
    ExtraKeys.ORIGIN,
    ExtraKeys.ORIGIN_SUBCATEGORY,
    ExtraKeys.NORMALIZED_URI,
    ExtraKeys.SOURCE_URI,
    ExtraKeys.ENCODED_WIDTH,
    ExtraKeys.ENCODED_HEIGHT,
    ExtraKeys.ENCODED_SIZE,
    ExtraKeys.MULTIPLEX_BITMAP_COUNT,
    ExtraKeys.MULTIPLEX_ENCODED_COUNT,
  })
  @interface ExtraKeys {
    String ORIGIN = "origin";
    String ORIGIN_SUBCATEGORY = "origin_sub";
    String SOURCE_URI = "uri_source";
    String NORMALIZED_URI = "uri_norm";
    String ENCODED_WIDTH = "encoded_width";
    String ENCODED_HEIGHT = "encoded_height";
    String ENCODED_SIZE = "encoded_size";
    /* number of deduped request in BitmapMemoryCacheKeyMultiplexProducer */
    String MULTIPLEX_BITMAP_COUNT = "multiplex_bmp_cnt";
    /* number of deduped request in EncodedCacheKeyMultiplexProducer */
    String MULTIPLEX_ENCODED_COUNT = "multiplex_enc_cnt";
  }

  /** @return image request that is being executed */
  ImageRequest getImageRequest();

  /** @return id of this request */
  String getId();

  /** @return optional id of the UI component requesting the image */
  @Nullable
  String getUiComponentId();

  /** @return ProducerListener2 for producer's events */
  ProducerListener2 getProducerListener();

  /** @return the {@link Object} that indicates the caller's context */
  Object getCallerContext();

  /** @return the lowest permitted {@link ImageRequest.RequestLevel} */
  ImageRequest.RequestLevel getLowestPermittedRequestLevel();

  /** @return true if the request is a prefetch, false otherwise. */
  boolean isPrefetch();

  /** @return priority of the request. */
  Priority getPriority();

  /** @return true if request's owner expects intermediate results */
  boolean isIntermediateResultExpected();

  /**
   * Adds callbacks to the set of callbacks that are executed at various points during the
   * processing of a request.
   *
   * @param callbacks callbacks to be executed
   */
  void addCallbacks(ProducerContextCallbacks callbacks);

  ImagePipelineConfig getImagePipelineConfig();

  EncodedImageOrigin getEncodedImageOrigin();

  void setEncodedImageOrigin(EncodedImageOrigin encodedImageOrigin);

  <E> void setExtra(@ExtraKeys String key, @Nullable E value);

  void putExtras(@Nullable Map<String, ?> extras);

  @Nullable
  <E> E getExtra(String key);

  @Nullable
  <E> E getExtra(String key, @Nullable E valueIfNotFound);

  Map<String, Object> getExtras();

  /** Helper to set {@link ExtraKeys#ORIGIN} and {@link ExtraKeys#ORIGIN_SUBCATEGORY} */
  void putOriginExtra(@Nullable String origin, @Nullable String subcategory);

  /** Helper to set {@link ExtraKeys#ORIGIN} */
  void putOriginExtra(@Nullable String origin);
}

 

這裡說下 ProducerContext 使用,這裡其實採用的是介面模式,然後相當於是面向介面程式設計,這樣後期在擴充套件的時候,也會變得更加方便。

類似的上下文還有 FrescoContext。

其實對於上下文,我們在設計原始碼的時候,也可以考慮下,有了 context 的存在,可以減少很多類之間的依賴,使得程式碼邏輯更加清晰。

 

5、ImagePipeline

ImagePipelineConfig: 這個可以說是把 ImagePipeline 用到的東西一網打盡;這個是用於使用者在使用 Fresco 的時候,可以進行對應的配置。

ImagePipeline: 發起請求的類(包括網路,本地快取,記憶體,回撥)以及解碼和非解碼的圖片,還有包括預取;

ImagePipelineFactory: 是一個單例。也就是說明所有的請求配置是一樣的。但是這樣的話,怎麼區分不同的請求,這塊還需要仔細看看的。url 是通過 imageRequest 來管理的,ImagePipeline 主要是負責管理其他方面的東西。包括快取等所有請求應該都是一樣的。然後在獲取 getDataSourceSupplier 的時候發起圖片請求。

imageRequest:imageRequest 包含 url 等相關資訊。會在 ImagePipeline 中構造一個 producerSequence。最終,producerSequence 和 settableProducerContext 會在 AbstractProducerToDataSourceAdapter 轉變為 DataSource;

 

6、ImagePipeline 和 producer 之間的關係

這個其實在 ImagePipeline 中解釋過了,他們相當於是一環套一環。

 

7、builder 模式的使用

ImageRequestBuilder: 用於構建 ImageRequest。

AbstractDraweeControllerBuilder: 使用得是泛型,將通用邏輯封裝在其內部;

PipelineDraweeControllerBuilder:controller 的邏輯在裡面;

這個其實主要是在前面的 config 裡面講過了,這裡就不再細說。

 

8、factory 模式的應用

DefaultDrawableFactory:生成動態靜態圖片;

PipelineDraweeControllerFactory:一個是建立controller,一個是建立內部controller;

ImagePipelineFactory:這個有點感覺是個容器,所有和 ImagePipeline 相關的都可以從裡面獲取到;

其他 factory 的類也有很多。就我個人理解,之所以用到 factory ,主要是為了遮蔽產品的具體實現,呼叫者只關心產品的介面。

 

相關文章