<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Flutter渲染之Widget、Element、RenderObject

          共 5494字,需瀏覽 11分鐘

           ·

          2021-07-21 13:31

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


          作者丨xiangzhihong

          來源:

          https://segmentfault.com/a/1190000039730403

          一、Flutter架構(gòu)

          眾所周知,F(xiàn)lutter是由Google推出的開源的高性能跨平臺框架,一個(gè)2D渲染引擎。在Flutter中,Widget是Flutter用戶界面的基本構(gòu)成單元,可以說一切皆Widget。與Weex和RN框架使用的JsCore轉(zhuǎn)化的中間層不同,F(xiàn)lutter采用的是全新的架構(gòu)方案,擁有自己的渲染引擎和Dart上層,每一層都建立在前一層的基礎(chǔ)之上,并且上層比下層的使用頻率更高,其框架架構(gòu)如下圖所示。



          可以看到,自下而上,F(xiàn)lutter分為Embedder、Engine和Framework三層。其中,Embedder是操作系統(tǒng)適配層,主要負(fù)責(zé)Surface渲染設(shè)置,線程設(shè)置,以及平臺插件等平臺相關(guān)特性的適配;Engine層負(fù)責(zé)圖形繪制、文字排版和提供Dart運(yùn)行時(shí),Engine層具有獨(dú)立虛擬機(jī),正是由于它的存在,F(xiàn)lutter程序才能運(yùn)行在不同的平臺上,實(shí)現(xiàn)跨平臺運(yùn)行;Framework層則是使用Dart編寫的一套基礎(chǔ)視圖庫,包含了動畫、圖形繪制和手勢識別等功能,是使用頻率最高的一層。

          • Flutter Embedder:Embedder是Flutter的操作系統(tǒng)適配層,又稱為嵌入層,通過該層可以把Flutter嵌入到各個(gè)不同的平臺上去。Embedder的主要工作包括Surface渲染設(shè)置、線程設(shè)置、事件循環(huán)以及插件的平臺適配等。

          • Flutter Engine:純 C++實(shí)現(xiàn)的 SDK,其中包括 Skia引擎、Dart運(yùn)行時(shí)、文字排版引擎等。它是 Dart的一個(gè)運(yùn)行時(shí),它可以以 JIT 或者 AOT的模式運(yùn)行 Dart代碼。這個(gè)運(yùn)行時(shí)還控制著 VSync信號的傳遞、GPU數(shù)據(jù)的填充等,并且還負(fù)責(zé)把客戶端的事件傳遞到運(yùn)行時(shí)中的代碼。

          • Flutter Framework:純 Dart實(shí)現(xiàn)的 SDK,提供了一整套自底向上的基礎(chǔ)庫, 用于處理動畫、繪圖和手勢。并且基于繪圖封裝了一套 UI組件庫,然后根據(jù) Material 和Cupertino兩種視覺風(fēng)格區(qū)分開來。在平時(shí)應(yīng)用開發(fā)中,與開發(fā)者打交道最多的就是這一層,并且最多的就是各種Widget。

          二、渲染流程

          不管是什么渲染框架,其基本的原理都是:一般以60Hz的固定頻率刷新,每一幀圖像繪制完成后,會繼續(xù)繪制下一幀,然后顯示器就會發(fā)出一個(gè)Vsync信號,按60Hz計(jì)算,屏幕每秒會發(fā)出60次這樣的信號。CPU計(jì)算好顯示內(nèi)容提交給GPU,GPU渲染好交給顯示器顯示。

          在Flutter中,渲染會用到很多的線程,主要是UI線程和GPU線程,下圖是Flutter App線程的運(yùn)作原理圖。

          下面重點(diǎn)看一下UI線程和GPU線程。

          UI Task Runner

          UI Task Runner用于執(zhí)行Root Isolate代碼,它運(yùn)行在線程對應(yīng)平臺的線程上,屬于子線程。同時(shí),Root isolate在引擎啟動時(shí)會綁定不少Flutter需要的函數(shù)方法,這些綁定的函數(shù)可以提交渲染幀給Engine層執(zhí)行渲染操作,下圖演示了Widgets生成Layer Tree的過程。

          對于每一幀,引擎通過Root Isolate通知Flutter Engine有幀需要渲染,平臺收到Flutter Engine通知后會創(chuàng)建對象和組件并生成一個(gè)Layer Tree,然后將生成的Layer Tree提交給Flutter Engine。此時(shí),只生成了需要繪制的內(nèi)容,并沒有執(zhí)行屏幕渲染,而Root Isolate就是負(fù)責(zé)將創(chuàng)建的Layer Tree繪制到屏幕上,因此如果線程過載會導(dǎo)致卡頓掉幀現(xiàn)象。

          除了用于處理渲染之外,Root Isolate還需要處理來自Native Plugins的消息響應(yīng)、Timers、MicroTasks和異步IO。如果確實(shí)有無法避免的繁重計(jì)算,建議將這些耗時(shí)的操作放到獨(dú)立的Isolate去執(zhí)行,從而避免應(yīng)用UI卡頓問題。

          GPU Task Runner

          GPU Task Runner用于執(zhí)行設(shè)備GPU指令,UI Task Runner創(chuàng)建的Layer Tree是跨平臺的。也就是說,Layer Tree提供了繪制所需要的信息,但是由誰來完成繪制它是不關(guān)心的。

          GPU Task Runner的主要責(zé)任就是負(fù)責(zé)將Layer Tree提供的信息轉(zhuǎn)化為平臺可執(zhí)行的GPU指令,同時(shí)它也負(fù)責(zé)管理每一幀繪制所需要的GPU資源,包括平臺Framebuffer的創(chuàng)建,Surface生命周期管理,以及Texture和Buffers的繪制時(shí)機(jī)等,下圖GPU Task Runner的工作流程。


          UI Runner和GPU Runner運(yùn)行在不同的線程。GPU Runner會根據(jù)目前幀執(zhí)行的進(jìn)度去向UI Runner請求下一幀的數(shù)據(jù),在任務(wù)繁重的時(shí)候還可能會出現(xiàn)UI Runner的延遲任務(wù)。不過這種調(diào)度機(jī)制的好處在于,確保GPU Runner不至于過載,同時(shí)也避免了UI Runner不必要的資源消耗。

          GPU Runner可以導(dǎo)致UI Runner的幀調(diào)度的延遲,GPU Runner的過載會導(dǎo)致Flutter應(yīng)用的卡頓,因此在實(shí)際使用過程中,建議為每一個(gè)Engine實(shí)例都新建一個(gè)專用的GPU Runner線程。

          三、Widget、Element 和 RenderObject

          要理解Flutter的渲染原理,那么就必須了解Widget、RenderObject 和 Element及其作用??偟膩碚f,F(xiàn)lutter調(diào)用runApp(rootWidget),將rootWidget傳給rootElement,做為rootElement的子節(jié)點(diǎn),生成Element樹,由Element樹生成Render樹,如下圖所示。

          從上面的介紹中,我們隱約知道了Widget、RenderObject 和 Element的作用,簡單的介紹一下。

          • Widget:Widget 的主要作用是用來保存 Element 信息的(包括布局、渲染屬性、事件響應(yīng)等信息),本身是不可變的,Element 也是根據(jù) Widget 里面保存的配置信息來管理渲染樹,以及決定自身是否需要執(zhí)行渲染。

          • RenderObject:RenderObject 做為渲染樹中的對象存在,主要作用是處理布局、繪制相關(guān)的事情,而繪制的內(nèi)容是Widget傳入的內(nèi)容。

          • Element:Element 可以理解為是其關(guān)聯(lián)的 Widget 的實(shí)例,存放視圖構(gòu)建的上下文數(shù)據(jù),可以通過遍歷Element來查看視圖樹,Element同時(shí)持有Widget和RenderObject對象。

          Flutter通過Widget樹中的每個(gè)控件創(chuàng)建不同類型的渲染對象,組成渲染對象樹,而渲染對象樹在Flutter中的展示分為四階段:布局、繪制、合成及渲染。其中,布局和繪制由RenderObject負(fù)責(zé)完成,F(xiàn)lutter采用深度優(yōu)先機(jī)制遍歷渲染樹對象,確定樹中每個(gè)對象的位置和尺寸,并把他們繪制到不同的圖層上,而合成及渲染則交給Skia完成。

          下圖展示了Widget、Element 和 RenderObject的關(guān)系。

          3.1 Widget

          在 Flutter 中,萬物皆是 Widget,無論是可見的還是功能型的,下面是官方對Widget的介紹。

          • Widget 的作用是用來保存 Element 的配置信息的。

          • Widget 本身是不可變的。

          • Element 根據(jù) Widget 里面保存的配置信息來管理渲染樹。

          • Widget 可以多次的插入到 Widget 樹中,每插入一次,Element 都要重新裝載一遍 Widget 。

          • Widget 里面的 key 屬性用來決定依賴這個(gè) Widget 的 Element 在 Element 樹中是更新還是移除。

          下面是Widget源碼。

          abstract class Widget extends DiagnosticableTree{
          const Widget({ this.key });
          final Key key;

          @protected
          Element createElement();

          static bool canUpdate(Widget oldWidget, Widget newWidget) {
          return oldWidget.runtimeType == newWidget.runtimeType
          && oldWidget.key == newWidget.key;
          }
          }

          Widget有兩個(gè)重要的方法,一個(gè)是通過 createElement 來創(chuàng)建 Element 對象的,一個(gè)是根據(jù) key 來決定更新行為的 canUpdate 方法。

          3.2 RenderObject

          • RenderObject 是做為渲染樹中的對象存在。

          • RenderObject 不定義約束關(guān)系,也就是不會對子控件的布局位置、大小等進(jìn)行管理。

          • RenderObject 中有一個(gè) parentData 屬性,這個(gè)屬性用來保存其孩子節(jié)點(diǎn)的特定信息,如子節(jié)點(diǎn)位置,這個(gè)屬性對其孩子是透明的。

          RenderObject的源碼如下。

          abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
          ParentData parentData;
          Constraints _constraints;
          void layout(Constraints constraints, { bool parentUsesSize = false }) {

          }
          void paint(PaintingContext context, Offset offset) { }

          void performLayout();
          void markNeedsPaint() {
          }
          }

          可以看出,RenderObject 的主要作用就是繪制和布局。RenderObject 在 Flutter 中的作用分為四個(gè)階段,即布局、繪制、合成和渲染。其中,布局和繪制在 RenderObject 中完成,F(xiàn)lutter 采用深度優(yōu)先機(jī)制遍歷渲染對象樹,確定樹中各個(gè)對象的位置和尺寸,并把它們繪制在不同的圖層上。繪制完畢后,合成和渲染的工作則交給 Skia 完成。

          3.3 Element

          • Element 是關(guān)聯(lián)的Widget 的實(shí)例,并且關(guān)聯(lián)在 Widget 樹的特定位置上。

          • Widget 是不可變的,一個(gè) Widget 可以同時(shí)用來配置多個(gè)子 Widget 樹,而 Element 就用來代表特定位置的 Widget 。

          • Widget 是不可變的,而 Element 是可變的,Element決定是否需要刷新界面。

          • 一些 Element 只能有一個(gè)子節(jié)點(diǎn),如 Container、Opacity、Center ,還有一些可以有多個(gè)子節(jié)點(diǎn),如 Column、Row 和 ListView 等。

          Element 擁有自己的生命周期:

          • Flutter framework 通過 Widget.createElement 來創(chuàng)建一個(gè) Element 。

          • 每當(dāng) Widget 創(chuàng)建并插入到 Widget 樹中時(shí),framework 就會通過 mount 方法來把這個(gè) widget 創(chuàng)建并關(guān)聯(lián)的 Element 插入到 Element 樹中。

          • 通過 attachRenderObject 方法來將 render objects 來關(guān)聯(lián)到 Render 樹上,這時(shí)可以認(rèn)為 Widget 已經(jīng)顯示在屏幕上了。

          • 每當(dāng)執(zhí)行了 rebuid 方法,Widget 代表的配置信息改變時(shí),framewrok 就會調(diào)用這個(gè)新的 Widget 的 update 方法執(zhí)行重繪。

          • 當(dāng) Element 的祖先想要移除一個(gè)子 Element 時(shí),可以通過 deactivateChild 方法,先把這個(gè) Element 從 樹中移除,然后將這個(gè) Element 加入到一個(gè)“不活躍元素列表”中,接著 framework 就會將這個(gè) element 從屏幕移除。

          總的來說,F(xiàn)lutter提出一切皆Widget,Widget 主要用來保存 Element 信息,而Element作用Widget 的實(shí)例,存放視圖構(gòu)建的上下文數(shù)據(jù),并且同時(shí)持有Widget和RenderObject對象,RenderObject的主要作用是處理布局、繪制相關(guān)的事情,確定Element樹中每個(gè)對象的位置和尺寸。

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99热亚洲精品 | 五月婷婷亚洲综合 | 中国一级片操逼的 | 国内一区 | 美女扒开粉嫩尿囗桶爽免费网站 |