Fresco源碼分析之Hierarchy

上篇文章咱們分析了Fresco中的DraweeView,對其中的一些原理以及方法進行了解析。在這過程當中咱們瞭解到,DraweeView中是經過DraweeHolder來統一管理的。而DraweeHolder又是用來統一管理相關的HierarchyController,若是想了解DraweeView相關的知識,能夠先看下個人前一篇文章Fresco源碼分析之DraweeView。今天這裏進一步來分析Fresco中的Hierarchyphp

GenericDraweeHierarchyBuilder

GenericDraweeView的構造方法中會調用*inflateHierarchy(context, atts)*方法來建立一個GenericDraweeHierarchyBuilder對象,經過調用該對象的build方法來生成一個Hierarchyandroid

若是你看了我上一篇文章,相信對這個方法你會感到很親切。git

因此這個類的主要做用是用來建立一個Hierarchy,經過builder模式來實例化包含相關信息的Hierarchy。若是你一步步深刻了解Fresco的話,相信你對builder模式也將習覺得常,由於後面你將常常與它碰面。這也是Fresco開源項目的主要設計模式。在該builder類中主要構建的信息有:github

  • Drawable相關:placeholderImageretryImagefailureImageprogressBarImagebackgroundoverlayspressedStateOverlay
  • ScaleType相關:與Drawable相對應的placeholderImageScaleTyperetryImageScaleType等等。
  • 其它:fadeDuration漸變過渡時間與RoundingParams圓角相關信息等等

GenericDraweeHierarchy

經過上面的GenericDraweeHierarchyBuilderbuild方法會建立一個GenericDraweeHierarchy對象。這就是咱們今天須要主要分析的類,也是Fresco的一個核心類。canvas

SettableDraweeHierarchy

首先咱們來分析一下它的類結構,它會實現SettableDraweeHierarchy接口,咱們進入該接口發現一共有6個接口方法,它們分別爲:設計模式

  1. void reset(); 從新初始化Hierarchy
  2. void setImage(Drawable drawable, float progress, boolean immediate); 設置實際須要展現的圖片,其中progress表示圖片的加載質量進度(在漸進式中會使用到)
  3. void setProgress(float progress, boolean immediate); 更新圖片加載進度
  4. void setFailure(Throwable throwable); 圖片加載失敗時調用,能夠設置failureImage
  5. void setRetry(Throwable throwable); 當圖片加載失敗時從新進行加載,能夠設置retryImage
  6. void setControllerOverlay(Drawable drawable); 用來設置圖層覆蓋

這些方法在GenericDraweeHierarchy中都會作出相應的實現,同時最終都會在對應的DraweeView中的Controller來調用。數組

至於這些方法中的實現細節,因爲代碼比較多,這裏就不一一列舉出來,你們能夠自行查看GenericDraweeHierarchy的源碼。緩存

DraweeHierarchy

上面的SettableDraweeHierarchy還有一個父類接口爲DraweeHierarchy,在這個接口中只有一個接口方法爲Drawable getTopLevelDrawable();。是否是對這個方法也有點熟悉呢?(路人甲:嗯,好像上篇文章說起過!)它是用來獲取視圖樹的最頂層視圖,其實說白了就是顯示出來的Drawable。它主要在DraweeHolder中調用,最終也會由void setHierarchy(DH hierarchy)void setController(@Nullable DraweeController draweeController) 來調用bash

super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
複製代碼

從而顯示須要展現的圖片。微信

下面咱們回到以前的GenericDraweeHierarchy類。在開始分析它以前,咱們先來了解一下它的另外一個感念-層級樹或者說圖層樹,我這裏就把它說成圖層樹吧。那麼咱們來看下Fresco的圖層樹是什麼樣的:

*  o RootDrawable (top level drawable)
 *  |
 *  +--o FadeDrawable
 *     |
 *     +--o ScaleTypeDrawable (placeholder branch, optional)
 *     |  |
 *     |  +--o Drawable (placeholder image)
 *     |
 *     +--o ScaleTypeDrawable (actual image branch)
 *     |  |
 *     |  +--o ForwardingDrawable (actual image wrapper)
 *     |     |
 *     |     +--o Drawable (actual image)
 *     |
 *     +--o null (progress bar branch, optional)
 *     |
 *     +--o Drawable (retry image branch, optional)
 *     |
 *     +--o ScaleTypeDrawable (failure image branch, optional)
 *        |
 *        +--o Drawable (failure image
複製代碼

根據上面所展現的層次結構,咱們能夠發現最底層是由RootDrawable來構成,它有一個直接子分支爲FadeDrawable。而在FadeDrawable中又有5個直接子分支,分別爲placeholder branchactual image branchprogressBar image branchretry image branchfailure image branch。至於這些image的做用相信不用我再多作說明了,這些image除了actual image是必需要明確指定的,其它的都是可選擇的配置。所以RootDrawableFadeDrawable是必定存在的。雖然其它的都是可選的配置,但不管你是否選擇了,它們的層級結構都會保留在圖層樹中。還有每個層級都有本身獨立的scale type,固然rounding(圓角)也是支持的。

其實除了這5個分支,與它們同一層次的還有background imageoverlay image,它們分別位於placeholder image以前與failure image以後。background相信都知道,由於圖片都支持backgroundsrcoverlay爲圖層覆蓋。至於爲何沒有在上面的結構中顯示,我這裏也不得而知,猜想多是這兩個並非Fresco主要經常使用的特性。

那麼這些圖層結構是經過layers數組來體現的,能夠來看下GenericDraweeHierarchy的源碼

GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) {
    mResources = builder.getResources();
    mRoundingParams = builder.getRoundingParams();
 
    mActualImageWrapper = new ForwardingDrawable(mEmptyActualImageDrawable);
 
    int numOverlays = (builder.getOverlays() != null) ? builder.getOverlays().size() : 1;
    numOverlays += (builder.getPressedStateOverlay() != null) ? 1 : 0;
 
    // layer indices and count
    int numLayers = OVERLAY_IMAGES_INDEX + numOverlays;
 
    // array of layers
    Drawable[] layers = new Drawable[numLayers];
    layers[BACKGROUND_IMAGE_INDEX] = buildBranch(builder.getBackground(), null);
    layers[PLACEHOLDER_IMAGE_INDEX] = buildBranch(
        builder.getPlaceholderImage(),
        builder.getPlaceholderImageScaleType());
    layers[ACTUAL_IMAGE_INDEX] = buildActualImageBranch(
        mActualImageWrapper,
        builder.getActualImageScaleType(),
        builder.getActualImageFocusPoint(),
        builder.getActualImageColorFilter());
    layers[PROGRESS_BAR_IMAGE_INDEX] = buildBranch(
        builder.getProgressBarImage(),
        builder.getProgressBarImageScaleType());
    layers[RETRY_IMAGE_INDEX] = buildBranch(
        builder.getRetryImage(),
        builder.getRetryImageScaleType());
    layers[FAILURE_IMAGE_INDEX] = buildBranch(
        builder.getFailureImage(),
        builder.getFailureImageScaleType());
    if (numOverlays > 0) {
      int index = 0;
      if (builder.getOverlays() != null) {
        for (Drawable overlay : builder.getOverlays()) {
          layers[OVERLAY_IMAGES_INDEX + index++] = buildBranch(overlay, null);
        }
      } else {
        index = 1; // reserve space for one overlay
      }
      if (builder.getPressedStateOverlay() != null) {
        layers[OVERLAY_IMAGES_INDEX + index] = buildBranch(builder.getPressedStateOverlay(), null);
      }
    }
 
    // fade drawable composed of layers
    mFadeDrawable = new FadeDrawable(layers);
     mFadeDrawable.setTransitionDuration(builder.getFadeDuration());
 
    // rounded corners drawable (optional)
    Drawable maybeRoundedDrawable =
        WrappingUtils.maybeWrapWithRoundedOverlayColor(mFadeDrawable, mRoundingParams);
 
    // top-level drawable
    mTopLevelDrawable = new RootDrawable(maybeRoundedDrawable);
    mTopLevelDrawable.mutate();
 
    resetFade();
  }
複製代碼

根據源碼能夠明顯的看出無論overlay是否存在,它都會保留圖層層次,固然若是存在的話,就根據實際狀況在OVERLAY_IMAGES_INDEX以後增長圖層層次。layers是一個Drawable數組,這裏經過Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 方法來建立對應的Drawable。在最後,建立了FadeDrawable並將layers傳遞給它,在這裏FadeDrawable的特性應該有點眉目了吧,其實它內部作的就是對Drawable數組進行操做。以後RootDrawable也出現了(關於Drawable文章後面會統一分析),這樣以前所提到的層級結構就造成了。

既然actual image是必定存在的,那麼在它真正展現以前image中的顯示用什麼來控制的呢?其實就是咱們以前所提到的SettableDraweeHierarchy來控制。在真實的圖片展現出來以前,它能夠用來展現placeholder image等相關圖層。具體的咱們能夠來看一下它裏面實現的一些方法。

setImage

這裏就拿void setImage(Drawable drawable, float progress, boolean immediate) 來進行深刻分析,那麼下面來看下它的源碼:

@Override
  public void setImage(Drawable drawable, float progress, boolean immediate) {
    drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources);
    drawable.mutate();
    mActualImageWrapper.setDrawable(drawable);
    mFadeDrawable.beginBatchMode();
    fadeOutBranches();
    fadeInLayer(ACTUAL_IMAGE_INDEX);
    setProgress(progress);
    if (immediate) {
      mFadeDrawable.finishTransitionImmediately();
    }
    mFadeDrawable.endBatchMode();
  }
複製代碼

首先該方法會使用WrappingUtils工具類而且結合RoundingParams參數來從新生成一個能夠附帶圓角的Drawable。而後將新的Drawable交由mActualImageWrapper,結合上面的層次結果分析,會很容易知道這就是須要真實顯示的圖層。它是一個ForwardingDrawable類型,該類主要是對傳入的目標Drawable進行相應的原生方法操做。下一步調用FadeDrawable的*beginBatchMode()方法,該方法的做用主要爲了圖層批處理作標記,防止在批處理時進行invalidate操做。直到最後的endBatchMode()方法調用以後才標識着圖層批處理操做結束。該批處理操做的目的是將actual image圖層顯示出來,因此首先調用fadeOutBranches()*方法:

private void fadeOutBranches() {
    fadeOutLayer(PLACEHOLDER_IMAGE_INDEX);
    fadeOutLayer(ACTUAL_IMAGE_INDEX);
    fadeOutLayer(PROGRESS_BAR_IMAGE_INDEX);
    fadeOutLayer(RETRY_IMAGE_INDEX);
    fadeOutLayer(FAILURE_IMAGE_INDEX);
  }
複製代碼

這裏對上述提到的全部圖層進行*fadeOutLayer()操做,繼續進入fadeOutLayer()*方法

private void fadeOutLayer(int index) {
    if (index >= 0) {
      mFadeDrawable.fadeOutLayer(index);
    }
  }
複製代碼

發現也很簡單,無非就是調用了FadeDrawable的*fadeOutLayer()*方法。

public void fadeOutLayer(int index) {
    mTransitionState = TRANSITION_STARTING;
    mIsLayerOn[index] = false;
    invalidateSelf();
  }
複製代碼

在以前已經提到過FadeDrawable本質上能夠理解爲時一個Drawable數組,內部都是圍繞着數組總體進行操做。對應的還有fadeInLayer()

private void fadeInLayer(int index) {
    if (index >= 0) {
      mFadeDrawable.fadeInLayer(index);
    }
  }
複製代碼

FadeDrawable中除了用來保存相應的Drawable數組mLayers,還有與其相對應的mIsLayerOn布爾數組,該數組用來標識各個Hierarchy中的圖層是否須要展現。因此*fadeOutLayer()是對index處的圖層進行隱藏標識。最終的顯隱操做都會轉化爲在void draw(Canvas canvas)*方法中進行alpha操做。

那麼再回到以前的*setImage()*方法中,*fadeOutBranches()是對相關的圖層進行隱藏標識,而後再經過fadeInLayer(ACTUAL_IMAGE_INDEX)方法改變actual image圖層的標識,將它改變成顯示狀態。最後若是有progressBar image圖層的話,也將會由setProgress(progress)*方法來體現。immediate是用來判斷是否以後的顯隱操做立馬實現或者漸變過渡實現(內部就是對alpha進行百分比操做,內部有個mDurationMs,該時間值也是文章開頭提到的builder中的fadeDuration)。這樣整個的實際圖片展現流程咱們已經分析完畢,因此Hierarchy中最重要的仍是對圖層概念的理解。下面再對GenericDraweeHierarchy中的一些其它方法進行簡要的說明:

  • Drawable buildActualImageBranch(Drawable drawable, @Nullable ScaleType scaleType, @Nullable PointF focusPoint, @Nullable ColorFilter colorFilter) 構建實際展現圖片的圖層分支,內部對於Drawable的建立仍是藉助WrappingUtils工具類
  • Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 構建除實際展現圖片以外的其它圖層分支,Drawable的建立也是藉助WrappingUtils工具類
  • void resetFade() 初始化圖層結構

主要的就這幾個吧,其它的都已經在上面分析流程中詳細說明了。

Drawable

最後再整理一下Fresco中的一些相關的自定義的Drawable子類

  1. ArrayDrawableDrawable數組的集合體,經過layers數組來管理Drawable,內部的都是對數組集合中的每個Drawable進行操做,相似與Android原生的LayerDrawable,只是它並不支持addremove操做
  2. ForwardingDrawable:對傳入的目標Drawable即操做對象進行封裝處理,該新類的方法能夠調用目標對象對應的方法,同時保留目標Drawable的各個狀態,不依賴與目標類的細節實現,提升新類的穩定性,該方式能夠稱之爲複合。Fresco中絕大多數自定義Drawable都是它的子類。
  3. AutoRotateDrawable:它繼承於ForwardingDrawable,實現的是對Drawable的旋轉操做
  4. FadeDrawable:它繼承於ArrayDrawable,以前也詳細提到過,Hierarchy中的主要圖層集合體。主要是經過mLayersmIsLayerOn數組來控制數組中各個Drawablealpha值,即顯隱
  5. MatrixDrawable:它繼承於ForwardingDrawable,顧名思義經過矩陣來改變Drawable狀態。
  6. OrientedDrawable:它也繼承於ForwardingDrawable,它不一樣於AutoRotateDrawable的是,它只支持90度的倍數角度旋轉。
  7. ProgressBarDrawable:進度條Drawable,支持橫豎方向。
  8. RoundedBitmapDrawable:繼承於BitmapDrawable,根據Bitmap來建立有關圓角的Drawable,主要在WrappingUtils類中使用,用例構建全新的圓角Drawable
  9. RoundedColorDrawable:繼承於Drawable,根據Color來建立圓角Drawable,主要在WrappingUtils類中使用,用例構建全新的圓角Drawable
  10. RoundedCornersDrawable:繼承於ForwardingDrawable,用於設計overLay覆蓋圖片圓角,主要在WrappingUtils類中使用
  11. ScaleTypeDrawable:繼承於ForwardingDrawable,用於縮放類型的Drawable

End

本篇文章主要是分析Fresco中有關Hierarchy相關的實現與原理,經過分析發現Hierarchy中都是對Drawable圖層進行處理,並無其它的緩存、請求之類的邏輯。因此若是你使用Fresco的時候只使用Hierarchy的話,就與別的ImageView沒有多大的區別,真正的圖層操做與緩存控制都在Controller中,因此下篇文章將進入Controller解析,來詳細瞭解它的實現細節。

Fresco源碼分析系列Github地址

Recommend

Android共享動畫兼容實現

Kotlin最佳實踐

RecyclerView下拉刷新與上拉更多

Android高仿微信之mvp實現(四)

php與android的簡單交互

tensorflow-梯度降低,有這一篇就足夠了

博客

相關文章
相關標籤/搜索