1. 程式人生 > >Fresco正傳(2):DraweeView分析

Fresco正傳(2):DraweeView分析

正文

既然要分析DraweeView,那麼就先看一下DraweeView的主要整合體系,然後在詳細分析一下都做了些什麼事情。

在看潘永強部落格的時候,其中他提到:原始碼的分析分為廣度和深度,先廣度再深度,但是都要適當,避免陷入無止境的廣度和深度的細節中去。

從這麼多篇博文來看,原始碼的分析從上至下、先父類後子類、先構造再細節的分析思路是非常棒的。但是,每個人分析習慣不一樣,就我來說更喜歡先從更易接觸的類再深入父類、先看類註釋再看詳細的方法。後續的部落格也會基本按照此種思路。

SimpleDraweeView是官方文件中,我們最先接觸類,也是最易使用的類。那麼就從這個類入手,一步一步分析。先上一下繼承體系圖,雖不是從上至下的分析,但是也需要充分了解體系結構。

SimpleDraweeViwe體系圖.png

好的開發框架,在命名方面往往做的非常好,起到見名知意作用。那麼,我們就從自己的開發經驗推測一下每個類都做了什麼。

SimpleDraweeView是簡單控制元件的意思,既然是簡單,就會提供出簡單明瞭、便易使用的方法,我想setImageURI(Uri uri)就是這樣一個方法。

GenericDraweeView是通用控制元件的意思,通過上一篇文章知道,DraweeView體系中,持有了DraweeHierarchyDraweeController的引用,那麼預設的東西,會不會是在這個類中提供的?。

DraweeView是什麼意思呢?,只從命名來看推測不出來什麼。

SimpleDrawee

要分析一個類,就先看他的類註釋。

/** 
* This view takes a uri as input and internally builds and sets a controller. 
* This class must be statically initialized in order to be used.
* If you are using the Fresco * image pipeline, use Fresco#initialize to do this. 
*/
  1. 這個控制元件接收URI作為輸入,並在內部建立和設定一個控制器。
  2. 這個類必須被靜態初始化才能夠被使用,如果你想使用Fresco預設的圖片管道來載入圖片,請使用Fresco的initlialize去做這件事。

相關注釋一定會體現為程式碼表示,我們很輕鬆就能夠找到做第一件事情的程式碼:

/**
  * Displays an image given by the uri.
  */
 public void setImageURI(Uri uri, @Nullable Object callerContext) {
     DraweeController controller = mSimpleDraweeControllerBuilder
             .setCallerContext(callerContext)
             .setUri(uri)
             .setOldController(getController())
             .build();
     setController(controller);
 }

通過uri展示一張圖片。在其內部,通過mSimpleDraweeControllerBuilder建造了一個DraweeController物件,並通過setController()方法,將控制器設定給了頂層的DraweeView類,當前從目前來看是這個樣子的。而一般以Builder結尾的都是使用了建造者模式的類。

這是一個學習設計模式的部落格

mSimpleDraweeControllerBuilder是一個成員變數,搜尋一下它在哪裡被賦值的。

來到了init()方法,此方法被各個建構函式所呼叫。

private void init() {
    if (isInEditMode()) {
        return;
    }
    Preconditions.checkNotNull(sDraweeControllerBuilderSupplier, "SimpleDraweeView was not initialized!");
    mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
}

雖然,程式碼不多,還是值得解釋一下。isInEditMode()是判斷當前是否是預覽模式或者是編輯模式,通常在AndrdioStudio中預覽佈局時,會碰到自定義控制元件顯示不出來的情況,加上這句話就可以了。

Preconditions.checkNotNull()是Fresco的工具類,從命名來看,就是檢查相關引用是否不為空,為空則直接報錯.

下面一句則是,從Supplier(提供者、供應商)中獲取一個SimpleDraweeContoller的建造器。特別說明的是,Supplier雖然不是某種設計模式之一,但是在Fresco被廣泛使用,它可是提供單個物件型別的類,在語義上它可能是一個工廠、建造、或其他任意的東西。

sDraweeControllerBuilderSupplier是一個靜態成員變數,它的初始化就聯絡到了本類所做的第二件事,使用Fresco.initialize(Context)來初始化本類。

既然需要適當在廣度上擴充套件,就接著看一下Fresco類。根據類的註釋,可以知道:

  1. 這個類是Fresco的切入點。
  2. 在使用這個類之前,你必須初始化此類。而一種簡單初始化的方式就是呼叫Fresco.initialize(Context)

既然有簡單的,那麼就有非簡單的,一共有兩種方式:

/** Initializes Fresco with the default config. */
 public static void initialize(Context context) {
   ImagePipelineFactory.initialize(context);
   initializeDrawee(context);
 }

 /** Initializes Fresco with the specified config. */
 public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) {
   ImagePipelineFactory.initialize(imagePipelineConfig);
   initializeDrawee(context);
 }

其中提到了預設的配置和指定的配置,這裡不做過多的深入,對於指定的配置(也就兩個引數的方法),你只需要知道在ImagePipelineConfig中有著大量的引數配置,以保證Fresco框架的可配置化,由於內部引數過多使用了建造者模式來解耦程式碼。

見名知意,ImagePipelineFactory.initialize(context)就是對配置初始化,這裡就先略過了。

這樣就引申到了initializeDrawee(Context context)方法:

private static void initializeDrawee(Context context) {
  sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
  SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
}

這段程式碼非常重要,它決定了Fresco使用的預設控制器是名為:PipelineDraweeController的類,先簡略看一下類註釋,該Controller是圖片載入器與Model的橋樑,也就是說,如何使用ImagePipeline載入圖片,並交給Model都是在這個類中控制的。當然,這也符合MVC模式的設計。

Drawee controller that bridges the image pipeline with {@link SettableDraweeHierarchy}.

接著就呼叫到了SimpleDraweeView靜態初始化的方法。這樣,從Fresco初始化到SimpleDraweeView的初始化至呼叫setImageUri()顯示一張圖片的流程就基本完整了。

給出一個示意圖,這個圖既非UML圖、也非流程圖,僅僅表達一些程式碼過程,個人覺得使用圖形更形象一些:

GenericDraweeView

從類註釋入手可以瞭解,本類的功能主要是通過解析XML的屬性來構建出一個通用的Hierarchy並進行設定。此外,還提供了一個設定控制元件寬高比的方法。

本類比較簡單,核心的方法在於inflateHierarchy(Context context, @Nullable AttributeSet attrs)

方法只做的三件事:
1. 解析XML屬性
2. 使用建造者模式構建GenericDraweeHierarchy
3. 將Hierarchy設定給DraweeView體系

  private void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
  Resources resources = context.getResources();

  // 解析XML屬性
  if (attrs != null) {
  }

  // 使用建造者模式,構建Hierarchy
  GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources);
  // ... ... 

  // 為DraweeView體系設定hierarchy
  setHierarchy(builder.build());
}

到此,值得注意的是。與DraweeView有關的DraweeHierarchyDraweeController均已出現,DraweeHierarchy對應的預設實現類是:GenericDraweeHierarchyDraweeController對應的預設實現類是:PipelineDraweeController

此外,本類做的第二件事,請參考博文:控制寬高比

DraweeView

先上一張圖,更簡潔的表達目的和想做的事。

還是先從類的註釋入手吧。從註釋中可以瞭解到如下幾件事:

  1. 該控制元件用於展示從DraweeHierarchy獲取的影象。
  2. 使用此控制元件之前,應該先為其設定DraweeHierarchy。由於建立一個DraweeHierarchy是一個昂貴的操作,推薦在建立的時候只做一次。
  3. 為了展示一張圖片,DraweeController必須被設定。
  4. Note:雖然這個控制元件是ImageView的直接子類,但是不支援ImageView的眾多方法。

在這個類中,即將接觸到一個其背後的男人DraweeHolder類,可以說DraweeView類中所做的所有操作,都是直接或者間接和DraweeHolder有關係的。DraweeView從其體系中獲取到的DraweeHierarchyDraweeController的資訊都被其轉交到了DraweeHolder類中。其實,這個就是一個解耦操作。假設,你想要自定一個控制元件,只想使用DraweeHierarchyDraweeController的功能,難道還自己持有這兩個類的引用? Just So So。DraweeHolder幫你省去了很多麻煩。

先看一下DraweeHolder是如何被建立的。

private void init(Context context) {
  ...
  mDraweeHolder = DraweeHolder.create(null, context);
  ...
}

看樣子平平無償,瀏覽該類的其他方法你會發現,大部分的操作都是將資訊轉交給DraweeHolder的操作。但是,請不要忽略以下兩個方法:

public void setController(@Nullable DraweeController draweeController) {
  mDraweeHolder.setController(draweeController);
  // 重點
  super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
public void setHierarchy(DH hierarchy) {
  mDraweeHolder.setHierarchy(hierarchy);
  // 重點
  super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}

注意到了兩個重點沒?mDraweeHolder.getTopLevelDrawable()獲取到的就是DraweeHierarchy.getTopLevelDraweeable()

從之前的分析可以得知,setHierarchy()是先被呼叫而後才是setController()
通過super.setImageDrawable(mDraweeHolder.getTopLevelDrawable())這句話,就將Fresco的圖片顯示與ImageView結合了起來,這也是圖片能夠顯示的原因。

如果你自定義控制元件了,千萬不要忘記模仿這兩個方法加上同樣的語句哦。

既然,DraweeView已經將職責都轉交給了DraweeHolder類,那麼就來看看DraweeHolder類做了些什麼。

還是先從類註釋入手:

  1. 這個類持有了DraweeControllerDraweeHierarchy
  2. 這個類便於自定義控制元件使用

在瀏覽類的所有方法時,onAttach()onDetach()方法引起了我的注意。

public void onAttach() {
    mEventTracker.recordEvent(Event.ON_HOLDER_ATTACH);
    mIsHolderAttached = true;
    attachOrDetachController();
}

public void onDetach() {
    mEventTracker.recordEvent(Event.ON_HOLDER_DETACH);
    mIsHolderAttached = false;
    attachOrDetachController();
}

通過註釋可以知道onAttach()的核心作用是獲取DraweeController以顯示影象;onDetach()的核心作用是釋放用於顯示影象的資源。他們都共同呼叫了attachOrDetachController()方法。

 private void attachOrDetachController() {
     if (mIsHolderAttached && mIsVisible && mIsActivityStarted) {
         attachController();
     } else {
         detachController();
     }
 }

你會發現,當同時滿足onAttach()被呼叫、控制元件可見時、Activity啟動時才會呼叫attachController()方法,既然需要同時滿足這麼多條件,想來這一定是個非常重要的方法。

可以說,Fresco核心載入的邏輯都是由這段程式碼引出的。

private void attachController() {
    if (mIsControllerAttached) {
        return;
    }
    mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
    mIsControllerAttached = true;
    if (mController != null && mController.getHierarchy() != null) {
        mController.onAttach();
    }
}

private void detachController() {
    if (!mIsControllerAttached) {
        return;
    }
    mEventTracker.recordEvent(Event.ON_DETACH_CONTROLLER);
    mIsControllerAttached = false;
    if (mController != null) {
        mController.onDetach();
    }
}

其中mIsControllerAttached是保證核心邏輯在一個控制元件中只被執行一次。在attachController()方法中,如果同時滿足了DraweeController不為空和DraweeHierarchy不為空,才會呼叫DraweeControlleronAttach()方法。

到了這裡,你是不是非常好奇在DraweeControlleronAttach()的方法中,到底做了怎麼樣重要的邏輯? (^__^) 嘻嘻……

最後

DraweeView的體系分析就基本完成了,如果覺得對您有幫助,多多留言哦。

相關推薦

Fresco正傳(2)DraweeView分析

正文 既然要分析DraweeView,那麼就先看一下DraweeView的主要整合體系,然後在詳細分析一下都做了些什麼事情。 在看潘永強部落格的時候,其中他提到:原始碼的分析分為廣度和深度,先廣度再深度,但是都要適當,避免陷入無止境的廣度和深度的細節中去。

Fresco正傳(3)DraweeHierarchy分析

前言 在正式開始分析之前,還是看一下DraweeHierarchy的繼承體系,做到心中有數。 看三個類的命名好像也只有SettableDraweeHierarchy可以猜出來一些 – 可設定的層次結構。 而GenericDraweeHierarchy你

Fresco前傳(2)原始碼分析 DraweeHierarchy/DraweeView/DraweeController

前言 Fresco的中文文件,最正宗的使用方法,當然在文件中尋找。 正文 DraweeHierarchy/DraweeView/DraweeController Fresco是一個圖片請求載入處理框架,整體架構是MVC模式 (DraweeHier

個人作業2網站分析之慕課網

配置 一次 adding tar 可能 經理 不同 自己的 同方 產品名   慕課網(網站) 選擇原因     目前使用比較頻繁的網站,也是程序員必備的一個網站 第一部分調研,評測   1、第一次上手體驗。 首頁分類清楚,可以根據課程,職業路徑,還可以使用模糊查詢,根據需

團隊作業2需求分析&原型設計

符號 原型設計工具 日期 clas max 獲得 痛苦 組合 人員 Deadline: 2017-11-5 22:00PM,以博客發表日期為準。 評分基準: 按時交 - 有分,檢查的項目包括後文的三個方面 需求分析 原型設計 編碼規範 晚交

Ionic開發2目錄分析及創建組件

n-n 數據 bootstra 只需要 public 是我 sele 元數據 output 上一篇我們創建好了一個新項目。 現在用VScode打開這個目錄並且觀察: node_modules :node 各類依賴包 resources :android/ios 資源(

Fresco正傳(7)如何手動清理Fresco的快取。

前言 這篇是隨手寫的,有博友在樓下提問相關問題。 這裡先把我知道的方案放這裡,以後有空詳細寫。 另外,請注意:雖然我找到了如何清理快取的方法,但是目前還未實際測試過。請自行測試哦。 正文 public class ImagePipelineCo

YII框架分析筆記2組件和事件行為管理

reac 設置 有變 相關 article class ces col cal Yii是一個基於組件、用於開發大型 Web 應用的高性能 PHP 框架。CComponent幾乎是所有類的基類,它控制著組件與事件的管理,其方法與屬性如下,私有變量$_e數據存放事件(evnet

軟工作業2硬幣遊戲——代碼的分析與改進

lis com color detail ogl .com commit mon atp 目的: Python 程序閱讀理解 學習Python 編碼風格指南中譯版(Google SOC), 改進Python程序 如何設計遊戲規則,使得慈善事業可持續。 地鐵口放置硬幣箱

個人作業2APP案例分析

似的 第一時間 為我 style href 程序出錯 朋友 bsp oca 鐵路12306案例分析 第一部分 調研, 評測 1.下載軟件並使用起來,描述最簡單直觀的個人第一次上手體驗。 鐵路12306app第一次使用的時候讓人感覺界面簡潔明

集美大學1414班軟件工程個人作業2——個人作業2APP案例分析

代碼 美工 總計 val 需求分析 get gui 背景 優點 一、作業鏈接 個人作業2:APP案例分析 二、博文要求 通過分析你選中的產品,結合閱讀《構建之法》,寫一篇隨筆,包含下述三個環節的所有要求。 第一部分 調研, 評測 下載軟件並使用起來,描述最簡

第二階段2.商業需求分析及BRD4.產品需求分析總結

總結 ima src 引導 產品需求 都是 篩選 img alt 產品的需求篩選 戰略定位要考慮公司的戰略問題。產品定位要分階段,各個階段的需求不同。 其實現在需求分析跟篩選都是非常快的。 不把需要當成需求,意思就是不要用戶說需要什麽就是什麽,用戶需要引導。

第二階段2.商業需求分析及BRD1.產品需求管理

管理 excel 有時 商業 重要 整理 需求分析 圖片 采集 產品經理需要投入大量的時間在需求方面的工作。 一張圖看出需求多麽重要。各個方面的人物對需求的誤解導致的後果。 首先收集需求 需求采集 可以用excel收集數據並整理 備註信息也很重要 有時候要跟提出人

第二階段2.商業需求分析及BRD5.商業需求文檔1

價值 產品介紹 fsd 計劃 線路 資源 結構 決策 9.png 三大文檔 FSD一般包含在PRD 1.BRD一般是去向決策層匯報 2.產品介紹的各項是可選的 不是必備的 產品線路圖就是roodmap。團隊一般是偏技術的團隊。 BRD案例。 痛點。定性的描述。不會非常

誰說菜鳥不會資料分析(入門篇)----- 學習筆記2(結構為王確定分析思路 4P 5W2H )

1、資料分析方法論 確定分析思路需要以營銷、管理等理論為指導,把這些跟資料分析相關的營銷、管理等理論統稱為資料分析方法論。 資料分析方法論主要用來指導資料分析師進行一次完整的資料分析,更多的是指資料分析思路,如從哪方面開展資料分析?各方面包含什麼內容和指標。 資料分析方法論主要

中國電信物聯網平臺使用2 DOME程式分析

"墨子號NB-IOT開發板"提供的dome: 程式只要分為延時,定時器,串列埠通訊…… 工程檔案在:…\STM32L1xx_StdPeriph_Lib_V1.3.1\Project\Test\MDK-ARM 程式初始化部分: delay_init();---------提供系統的延時功能 L

C++解析(2)進化後的 const 分析

0.目錄 1.C語言中的const 2.C++中的const 3.對比 3.1 C語言與C++中的const 3.2 C++中的const與巨集定義 4.小結 1.C語言中的const const修飾的變數是只讀的,本質還是變數 const修飾的區域性變數在棧上分配空間

誰說菜鳥不會資料分析(工具篇)----- 學習筆記2(結構為王確定分析思路)

1、資料分析方法論 確定分析思路需要以營銷、管理等理論為指導,把這些跟資料分析相關的營銷、管理等理論統稱為資料分析方法論。 資料分析方法論主要用來指導資料分析師進行一次完整的資料分析,更多的是指資料分析思路,如從哪方面開展資料分析?各方面包含什麼內容和指標。 資料分析方

QIIME 2可重複、互動和擴充套件的微生物組資料分析流程

文章目錄 QIIME2:可重複、可互動、適用範圍廣和可擴充套件的微生物組資料科學 摘要 正文 圖1. 互動式視覺化工具 圖2. 迭代記錄資料來源確保分析可重複 程式碼可用 線上方法

無鎖執行緒通訊(2)例程與分析

 首先,我們來看一個例子。   1 //    互動通道“沒有貨”狀態  2 #define COMMUNICATIONCHANNEL_STATE_THINGSNOTEXISTS 0  3 //    互動通道“有貨”狀態  4 #define COMMUNICATIONCHANNEL_STATE_