最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳細分析Fresco源碼之圖片加載流程

 更新時間:2021年01月28日 15:02:09   作者:huansky  
Fresco是一個強大的圖片加載組件,使用它之后,你不需要再去關心圖片的加載和顯示這些繁瑣的事情!它支持Android2.3及以后的版本

一、概述

Fresco 是一個強大的圖片加載組件。使用它之后,你不需要再去關心圖片的加載和顯示這些繁瑣的事情! 支持 Android 2.3 及以后的版本。如果需要了解 Fresco 的使用可以訪問 Fresco 使用文檔。

Fresco是一個功能完善的圖片加載框架,在Android開發(fā)中有著廣泛的應用,那么它作為一個圖片加載框架,有哪些特色讓它備受推崇呢?

  • 完善的內存管理功能,減少圖片對內存的占用,即便在低端機器上也有著不錯的表現。
  • 自定義圖片加載的過程,可以先顯示低清晰度圖片或者縮略圖,加載完成后再顯示高清圖,可以在加載的時候縮放和旋轉圖片。
  • 自定義圖片繪制的過程,可以自定義谷中焦點、圓角圖、占位圖、overlay、進圖條。
  • 漸進式顯示圖片。
  • 支持Gif。
  • 支持Webp。

Fresco的組成結構還是比較清晰的,大致如下圖所示:

其實這兩張圖來自不同的文章,但是我覺得兩者的分層實際上基本是一樣的。只是一個比較概括,一個比價具體,將兩者擺在一起,更有助于大家去理解其實現細節(jié)。當然除了 UI 和加載顯示部分外,還有 Gif,動態(tài)圖片等內容,以及對應圖片解碼編碼邏輯等。這部分不打算去講解,因為這部分雖然也是源碼很重要的一部分,但是這部分需要相關專業(yè)知識才好說明白,此外且涉及到 C++ 代碼。

下面結合代碼分別解釋一下上面各模塊的作用以及大概的工作原理。

二、DraweeView

它繼承自ImageView,Fresco加載圖片各個階段過程中圖片顯示的載體,比如在加載圖片過程中它顯示的是占位圖、在加載成功時切換為目標圖片。不過后續(xù)官方可能不再讓這個類繼承ImageView,所以該類并不支持ImageView 的 setImageXxx, setScaleType 以及其他類似的方法。目前DraweeViewImageView唯一的交集是:它利用ImageView來顯示 Drawable:

//DraweeView.setController()
public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());  //super 就是 ImageView
}

//DraweeHolder.getTopLevelDrawable()
public @Nullable Drawable getTopLevelDrawable() {
    return mHierarchy == null ? null : mHierarchy.getTopLevelDrawable(); // mHierarchy 是 DraweeHierachy,
}

DraweeView.setController()會在Fresco加載圖片時會調用。其實在這里可以看出Fresco的圖片顯示原理是 :利用ImageView顯示DraweeHierachyTopLevelDrawable。上面這段代碼引出了UI 層中另外兩個關鍵類:DraweeHolderDraweeHierachy。

三、DraweeHierachy

可以說它是Fresco圖片顯示的實現者。它的輸出是Drawable,這個Drawable會被DraweeView拿來顯示(上面已經說了)。它內部有多個Drawable,當前顯示在DraweeViewDrawable叫做TopLevelDrawable。在不同的圖片加載階段,TopLevelDrawable是不同的(比如加載過程中是 placeholder,加載完成是目標圖片)。具體的Drawable切換邏輯是由它來具體實現的。

它是由DraweeController直接持有的,因此對于不同圖片顯示的切換操作具體是由DraweeController來直接操作的。

四、DraweeHolder

可以把它理解為DraweeView、DraweeHierachyDraweeController這 3 個類之間的粘合劑, DraweeView 并不直接和DraweeController 和DraweeHierachy 直接接觸,所有的操作都是通過它傳過去。這樣,后續(xù)將 DraweeView 的父類改為 View,也不會影響到其他類。DraweeView 作為 View 可以感知點擊和生命周期,通過DraweeHolder 來控制其他兩個類的操作。

想想如果是你,你會抽出 DraweeHolder 這樣一個類嗎?實際上,這里對我們平時開發(fā)也是有所借鑒,嚴格控制每一個類之間的關系,可以引入一些一些中間類,讓類與類之間的關系耦合度降低,方便日后迭代。

具體引用關系如下圖:

它的主要功能是: 接收DraweeView的圖片加載請求,控制ProducerSequence發(fā)起圖片加載和處理流程,監(jiān)聽ProducerSequence加載過程中的事件(失敗、完成等),并更新最新的DrawableDraweeHierachy。

五、DraweeController 的構造邏輯

FrescoDraweeController是通過PipelineDraweeControllerBuilderSupplier 獲取的。Fresco在初始化時會調用下面的代碼:

// Fresco.java
private static void initializeDrawee(Context context, @Nullable DraweeConfig draweeConfig) {
    sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
}

sDraweeControllerBuilderSupplier 是靜態(tài)變量,也就是說其在只會初始一次。所有的DraweeController都是通過調用sDraweecontrollerbuildersupplier.get() 得到的。

private void init(Context context, @Nullable AttributeSet attrs) {
    try {
      if (FrescoSystrace.isTracing()) {
        FrescoSystrace.beginSection("SimpleDraweeView#init");
      }
      if (isInEditMode()) {
        getTopLevelDrawable().setVisible(true, false);
        getTopLevelDrawable().invalidateSelf();
      } else {
        Preconditions.checkNotNull(
            sDraweecontrollerbuildersupplier, "SimpleDraweeView was not initialized!");
        mControllerBuilder = sDraweecontrollerbuildersupplier.get(); // 調用一次就會創(chuàng)建一個新的實例
    }
    // ...... 省略其他代碼  
}

Fresco每次圖片加載都會對應到一個DraweeController,一個DraweeView的多次圖片加載可以復用同一個DraweeController:

SimpleDraweeView.java

public void setImageURI(Uri uri, @Nullable Object callerContext) {
    DraweeController controller =
        mControllerBuilder
            .setCallerContext(callerContext)
            .setUri(uri) //設置新的圖片加載路徑
            .setOldController(getController())  //復用 controller
            .build(); 
    setController(controller);
}

所以一般情況下 : 一個DraweeView對應一個DraweeController。

六、通過 DataSource 發(fā)起圖片加載

在前面已經說了DraweeController是直接持有DraweeHierachy,所以它觀察到ProducerSequence的數據變化是可以很容易更新到DraweeHierachy(具體代碼先不展示了)。那它是如何控制ProducerSequence來加載圖片的呢?其實DraweeController并不會直接和ProducerSequence發(fā)生關聯。對于圖片的加載,它直接接觸的是DataSource,由DataSource進而來控制ProducerSequence發(fā)起圖片加載和處理流程。下面就跟隨源碼來看一下DraweeController是如果通過DataSource來控制ProducerSequence發(fā)起圖片加載和處理流程的。

// AbstractDraweeController.java
protected void submitRequest() {
    mDataSource = getDataSource(); 
    final DataSubscriber<T> dataSubscriber = new BaseDataSubscriber<T>() { //可以簡單的把它理解為一個監(jiān)聽者
        @Override
        public void onNewResultImpl(DataSource<T> dataSource) { //圖片加載成功
            ...
        }
        ...
    };
    ...
    mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor); //mUiThreadImmediateExecutor是指 dataSubscriber 回調方法運行的線程,這里是主線程
}

DataSource是什么呢? getDataSource()最終會調用到:

// PipelineDraweeControllerBuilder 
 protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
      DraweeController controller,
      String controllerId,
      ImageRequest imageRequest,
      Object callerContext,
      AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
    return mImagePipeline.fetchDecodedImage(
        imageRequest,
        callerContext,
        convertCacheLevelToRequestLevel(cacheLevel),
        getRequestListener(controller),
        controllerId);
  }
// CloseableProducerToDataSourceAdapter<T>
public static <T> DataSource<CloseableReference<T>> create(
      Producer<CloseableReference<T>> producer,
      SettableProducerContext settableProducerContext,
      RequestListener2 listener) {
   
    CloseableProducerToDataSourceAdapter<T> result =
        new CloseableProducerToDataSourceAdapter<T>(producer, settableProducerContext, listener);return result;
  }

所以DraweeController最終拿到的DataSourceCloseableProducerToDataSourceAdapter。這個類在構造的時候就會啟動圖片加載流程(它的構造方法會調用producer.produceResults(...),這個方法就是圖片加載的起點,我們后面再看)。

這里我們總結一下FrescoDataSource的概念以及作用:在FrescoDraweeController每發(fā)起一次圖片加載就會創(chuàng)建一個DataSource,這個DataSource用來提供這次請求的數據(圖片)。DataSource只是一個接口,至于具體的加載流程Fresco是通過ProducerSequence來實現的。

七、Fresco圖片加載前的邏輯

了解了上面的知識后,我們過一遍圖片加載的源碼(從 UI 到 DraweeController),來理一下目前所了解的各個模塊之間的聯系。我們在使用Fresco加載圖片時一般是使用這個API:SimpleDraweeView.setImageURI(imageLink),這個方法最終會調用到:

// SimpleDraweeView.java
public void setImageURI(Uri uri, @Nullable Object callerContext) {
    DraweeController controller = mControllerBuilder
            .setCallerContext(callerContext)
            .setUri(uri)
            .setOldController(getController())
            .build();    //這里會復用 controller
    setController(controller);
}

public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());  
}

即每次加載都會使用DraweeControllerBuilderbuild一個DraweeController。其實這個DraweeController默認是復用的,這里的復用針對的是同一個SimpleDraweeView

。然后會把DraweeController設置給DraweeHolder,并在加載開始默認是從DraweeHolder獲取TopLevelDrawable并展示到DraweeView。繼續(xù)看一下DraweeHolder的邏輯:

// DraweeHolder.java
public @Nullable Drawable getTopLevelDrawable() {
    return mHierarchy == null ? null : mHierarchy.getTopLevelDrawable();
}


/** Sets a new controller. */
public void setController(@Nullable DraweeController draweeController) {
boolean wasAttached = mIsControllerAttached;  
    if (wasAttached) {
        detachController();  
    }  
    // Clear the old controller 
    if (isControllerValid()) {    
        mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);   
        mController.setHierarchy(null);  
    }  
    mController = draweeController; 
    // 注意這里是只有確定已經 attached 才會調用,也就是才回去加載圖片  
    if (wasAttached) {
        attachController(); 
    }
}

DraweeHolder.setController()中把DraweeHierachy設置給DraweeController,并重新attachController(),attachController()主要調用了DraweeController.onAttach():

// AbstractDraweeController.java
public void onAttach() {
    ...
    mIsAttached = true;
    if (!mIsRequestSubmitted) {
      submitRequest();
    }
}

protected void submitRequest() {
    mDataSource = getDataSource(); 
    final DataSubscriber<T> dataSubscriber = new BaseDataSubscriber<T>() { //可以簡單的把它理解為一個監(jiān)聽者
        @Override
        public void onNewResultImpl(DataSource<T> dataSource) { //圖片加載成功
            ...
        }
        ...
    };
    ...
    mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor); //mUiThreadImmediateExecutor是指 dataSubscriber 回調方法運行的線程,這里是主線程
}

即通過submitRequest()提交了一個請求,這個方法我們前面已經看過了,它所做的主要事情就是,構造了一個DataSource。這個DataSource我們經過追蹤,它的實例實際上是CloseableProducerToDataSourceAdapter。CloseableProducerToDataSourceAdapter在構造時就會調用producer.produceResults(...),進而發(fā)起整個圖片加載流程。

用下面這張圖總結從SimpleDraweeView->DraweeController的圖片加載邏輯:

到這里我們梳理完了Fresco在真正發(fā)起圖片加載前所走的邏輯,那么Fresco的圖片加載流程是如何控制的呢?到底經歷了哪些步驟呢?

八、Producer

Fresco中有關圖片的內存緩存、解碼、編碼、磁盤緩存、網絡請求都是在這一層實現的,而所有的實現的基本單元是Producer,所以我們先來理解一下Producer:

看一下它的定義:

/**
 * <p> Execution of image request consists of multiple different tasks such as network fetch,
 * disk caching, memory caching, decoding, applying transformations etc. Producer<T> represents
 * single task whose result is an instance of T. Breaking entire request into sequence of
 * Producers allows us to construct different requests while reusing the same blocks.
 */
public interface Producer<T> {

  /**
   * Start producing results for given context. Provided consumer is notified whenever progress is made (new value is ready or error occurs).
   */
  void produceResults(Consumer<T> consumer, ProducerContext context);
}

結合注釋我們可以這樣定義Producer的作用:一個Producer用來處理整個Fresco圖片處理流程中的一步,比如從網絡獲取圖片、內存獲取圖片、解碼圖片等等。而對于Consumer可以把它理解為監(jiān)聽者,看一下它的定義:

public interface Consumer<T> {
/**
   * Called by a producer whenever new data is produced. This method should not throw an exception.
   *
   * <p>In case when result is closeable resource producer will close it after onNewResult returns.
   * Consumer needs to make copy of it if the resource must be accessed after that. Fortunately,
   * with CloseableReferences, that should not impose too much overhead.
   *
   * @param newResult
   * @param status bitwise values describing the returned result
   * @see Status for status flags
   */
  void onNewResult(T newResult, @Status int status);

  /**
   * Called by a producer whenever it terminates further work due to Throwable being thrown. This
   * method should not throw an exception.
   *
   * @param t
   */
  void onFailure(Throwable t);

  /** Called by a producer whenever it is cancelled and won't produce any more results */
  void onCancellation();

  /**
   * Called when the progress updates.
   *
   * @param progress in range [0, 1]
   */
  void onProgressUpdate(float progress);
}

Producer的處理結果可以通過Consumer來告訴外界,比如是失敗還是成功。

九、Producer 的組合

一個ProducerA可以接收另一個ProducerB作為參數,如果ProducerA處理完畢后可以調用ProducerB來繼續(xù)處理。并傳入Consumer來觀察ProducerB的處理結果。比如Fresco在加載圖片時會先去內存緩存獲取,如果內存緩存中沒有那么就網絡加載。這里涉及到兩個Producer分別是BitmapMemoryCacheProducerNetworkFetchProducer,假設BitmapMemoryCacheProducerProducerA,NetworkFetchProducerProducerB。我們用偽代碼看一下他們的邏輯:

// BitmapMemoryCacheProducer.java

public class BitmapMemoryCacheProducer implements Producer<CloseableReference<CloseableImage>> {

    private final Producer<CloseableReference<CloseableImage>> mInputProducer;

    // 我們假設 inputProducer 在這里為NetworkFetchProducer
    public BitmapMemoryCacheProducer(...,Producer<CloseableReference<CloseableImage>> inputProducer) { 
        ...
        mInputProducer = inputProducer;
    }

    @Override
    public void produceResults(Consumer<CloseableReference<CloseableImage>> consumer,...) {
        CloseableReference<CloseableImage> cachedReference = mMemoryCache.get(cacheKey);

        if (cachedReference != null) { 
            //從緩存中獲取成功,直接通知外界
            consumer.onNewResult(cachedReference, BaseConsumer.simpleStatusForIsLast(isFinal));
            return; 
        }
        //包了一層Consumer,即mInputProducer產生結果時,它自己可以觀察到
        Consumer<CloseableReference<CloseableImage>> wrappedConsumer = wrapConsumer(consumer..); 
        //網絡加載
        mInputProducer.produceResults(wrappedConsumer, producerContext); 
    }
}
// NetworkFetchProducer.java

public class NetworkFetchProducer implements Producer<EncodedImage> {

   // 它并沒有 inputProducer, 對于 Fresco 的圖片加載來說如果網絡都獲取失敗,那么就是圖片加載失敗了

    @Override
    public void produceResults(final Consumer<CloseableReference<CloseableImage>> consumer,..) {

       // 網路獲取
       //   ...
        if(獲取到網絡圖片){
            notifyConsumer(...); //把結果通知給consumer,即觀察者
        }
        ...
    }
}

代碼可能不是很好理解,可以結合下面這張圖來理解這個關系:

Fresco可以通過組裝多個不同的Producer來靈活的定義不同的圖片處理流程的,多個Producer組裝在一塊稱為ProducerSequence (Fresco 中并沒有這個類哦)。一個ProducerSequence一般定義一種圖片處理流程,比如網絡加載圖片的ProducerSequence叫做NetworkFetchSequence,它包含多個不同類型的Producer。

十、網絡圖片加載的處理流程

Fresco中不同的圖片請求會有不同的ProducerSequence來處理,比如網絡圖片請求:

// ProducerSequenceFactory.java
private Producer<CloseableReference<CloseableImage>> getBasicDecodedImageSequence(ImageRequest imageRequest) {
    switch (imageRequest.getSourceUriType()) {
        case SOURCE_TYPE_NETWORK: return getNetworkFetchSequence();
        ...
}

所以對于網絡圖片請求會調用getNetworkFetchSequence:

/**
* swallow result if prefetch -> bitmap cache get -> background thread hand-off -> multiplex ->
* bitmap cache -> decode -> multiplex -> encoded cache -> disk cache -> (webp transcode) ->
* network fetch.
*/
private synchronized Producer<CloseableReference<CloseableImage>> getNetworkFetchSequence() {
    ...
    mNetworkFetchSequence = new BitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence());
    ...
    return mNetworkFetchSequence;
}

getNetworkFetchSequence會經過重重調用來組合多個Producer。這里我就不追代碼邏輯了,直接用下面這張圖來描述Fresco網絡加載圖片的處理流程:

可以看到Fresco的整個圖片加載過程還是十分復雜的。并且上圖我只是羅列一些關鍵的Producer,其實還有一些我沒有畫出來。

十一、總結

為了輔助理解,再提供一張總結的流程圖,將上面整個過程都放在里面了。后續(xù)的系列文章會詳細介紹 UI 和圖片加載過程,希望通過閱讀其源碼來詳細了解內部的代碼邏輯以及設計思路。

其實我們在閱讀別人源碼的時候,除了要知道具體的細節(jié)之外,也要注意別人的模塊設計,借鑒其設計思想。然后想想如果是你在設計的時候,你會怎么劃分模塊,如何將不同的模塊聯系起來。

當模塊劃分后,里面的子模塊又是如何劃分的,它們之間協作關系如何保持。

以上就是詳細分析Fresco源碼之圖片加載流程的詳細內容,更多關于Fresco 圖片加載流程的資料請關注腳本之家其它相關文章!

相關文章

最新評論

老河口市| 湛江市| 沁水县| 大庆市| 临朐县| 铁力市| 梓潼县| 邹平县| 隆昌县| SHOW| 永春县| 明光市| 平乡县| 西乌珠穆沁旗| 万宁市| 玉屏| 平顺县| 云梦县| 千阳县| 大宁县| 新绛县| 乌恰县| 墨竹工卡县| 绍兴市| 长泰县| 遂溪县| 章丘市| 寻乌县| 虹口区| 石家庄市| 福海县| 河津市| 道孚县| 南平市| 望都县| 邵阳县| 永福县| 温宿县| 瑞安市| 佛学| 玉环县|