Widget、Element和RenderObject之間的轉(zhuǎn)化

開篇

image

Flutter中頁面的渲染渲染離不開三個(gè)重要的元素:Widget、Element、RenderObject。是一個(gè)從Widget到Element再到RenderObject的過程。而具體到源碼中,這個(gè)轉(zhuǎn)換的工作時(shí)如何實(shí)現(xiàn)的呢,今天我們就來跟著源碼簡(jiǎn)略分析一下。為了更流程的分析它們?nèi)咧g的關(guān)系,我們先根據(jù)Framework層源碼的注釋了解一下它們,然后再?gòu)腇lutter項(xiàng)目入口進(jìn)行分析。

Widget、Element和RenderObject

根據(jù)源碼注釋我么可以大致知道:

  1. Widget是對(duì)UI的一種廉價(jià)的、不可變的,描述(如何配置子樹)Element的配置,并可以生成Element
  2. Widget本身沒有狀態(tài),所有屬性都是final,Element持有WidgetRenderObject的實(shí)例
  3. 給定Widget可不斷加入到樹中不同的位置,并生成Element作為它在樹中的實(shí)例,它們是一對(duì)多的關(guān)系
  4. RenderObject是實(shí)際的渲染對(duì)象,Element就可以理解為將變化的Widget轉(zhuǎn)化為較為穩(wěn)定的RenderObject

Flutter工程的入口

新建一個(gè)Flutter項(xiàng)目后,我們會(huì)看到其工程目錄如下

image

其中,main.dart文件中有main函數(shù)作為程序的入口:

void main() {
  runApp(MyApp());
}

其中MyApp()就是一個(gè)Widget,我們忽略其他代碼,只關(guān)心它在代碼中的走向,發(fā)現(xiàn)它最終作為一個(gè)child用于RenderObjectToWidgetAdapter的初始化,并且通過attachToRenderTree方法生成了一個(gè)Element。

  void attachRootWidget(Widget rootWidget) {
    _readyToProduceFrames = true;
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
  }  

繼續(xù)看它的attachToRenderTree方法:

 RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement();
        assert(element != null);
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        element.mount(null, null);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

可以發(fā)現(xiàn):在我們創(chuàng)建的“根”Widget之前,會(huì)有一個(gè)RenderObjectToWidgetAdapter作為真正的根Widget,而與之對(duì)應(yīng)的是一個(gè)RenderObjectToWidgetElement作為根Element。通過調(diào)用createElement方法初始化與之對(duì)應(yīng)的Element。該方法在抽象類Widget中被定義,可被子類重寫。
而觀察Element的初始化方式以及定義:

Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

不難發(fā)現(xiàn):Element初始化時(shí)接受了當(dāng)前的Widget并作為私有變量持有。那么我們的Widget,MyApp是在何時(shí)轉(zhuǎn)換成Element的呢?

自上而下的調(diào)用創(chuàng)建Element

繼續(xù)追蹤attachToRenderTree方法源碼,我們發(fā)現(xiàn)在Element被初始化后,會(huì)調(diào)用element.mount(null, null)方法(只保留關(guān)鍵代碼),該代碼在framework.dart文件中的Element類中。

void mount(Element parent, dynamic newSlot) {
    assert(parent == null);
    super.mount(parent, newSlot);
    _rebuild();
  }
void _rebuild() {
    try {
      //_child是其子Element,也就是`MyApp`將要生成的Element,此時(shí)進(jìn)行初始化
      //widget.child是我們的`MyApp`
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } catch (exception, stack) {
      ...
    }
  }
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    if (newWidget == null) {
       //如果newWidget為空,說明沒有子樹,返回null
      if (child != null)
        deactivateChild(child);
      return null;
    }
    Element newChild;
    //此時(shí)_child為null
    if (child != null) {
      ...
    } else {
      //初始化
      newChild = inflateWidget(newWidget, newSlot);
    }
    return newChild;
  }
  
Element inflateWidget(Widget newWidget, dynamic newSlot) {
    final Key key = newWidget.key;
    if (key is GlobalKey) {
      final Element newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        assert(newChild._parent == null);
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
        newChild._activateWithParent(this, newSlot);
        final Element updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
        return updatedChild;
      }
    }
    //調(diào)用`MyApp`的`createElement`方法進(jìn)行初始化
    final Element newChild = newWidget.createElement();
    assert(() {
      _debugCheckForCycles(newChild);
      return true;
    }());
    //執(zhí)行monut方法,繼續(xù)下一個(gè)Widget的轉(zhuǎn)換
    newChild.mount(this, newSlot);
    assert(newChild._debugLifecycleState == _ElementLifecycle.active);
    return newChild;
  }

可以發(fā)現(xiàn),當(dāng)我們的程序啟動(dòng)時(shí),runApp會(huì)創(chuàng)建一個(gè)根Widget并調(diào)用其createElement方法生成element。并執(zhí)行element的mount方法。之后,element會(huì)讀取并判斷自己所持有的Widget對(duì)象是否有子Widget,就是上面代碼中widget.child。當(dāng)發(fā)現(xiàn)child不為空,就會(huì)繼續(xù)執(zhí)行createElement()方法,并執(zhí)行mount()方法進(jìn)入下一次子樹。依次類推直至遍歷完所有子樹。

依舊是自上而下的調(diào)用卻有點(diǎn)不一樣

在追蹤上述代碼時(shí),我們并沒有直接發(fā)現(xiàn)類似createElementcreateRenderObject()方法被調(diào)用,我們觀察RenderObjectToWidgetElement的繼承關(guān)系:

image

最終可在RenderObjectElementmount()方法里看到這樣的代碼:

@override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    ....
    _renderObject = widget.createRenderObject(this);
    ....
  }

在這里,我們發(fā)現(xiàn)了初始化RenderObject的地方??梢园l(fā)現(xiàn),RenderObjectWidget實(shí)在Element里被初始化,并且和Widget一樣被Element所持有。并且,采取和Element一樣,自上而下的不斷遍歷整個(gè)樹進(jìn)行初始化。
但是有一點(diǎn)我們需要注意:
createElement()方法在所有widget的基類Widget中被定義了。但是createRenderObject()方法是在Widget的子類RenderObjectWidget中被定義的,于此同時(shí),createRenderObject()方法也只會(huì)在RenderObjectElement中被調(diào)用。
這說明:每個(gè)Widget都會(huì)生成一個(gè)Element,但并不是每個(gè)Element都會(huì)創(chuàng)建一個(gè)RenderObject,只有RenderObjectWidget類型的子類的Widget創(chuàng)建的繼承自RenderObjectElement類型的element才會(huì)創(chuàng)建RenderObject。也就是說,widget樹、element樹和RenderObject樹并不是一是一對(duì)應(yīng)的。

image

總結(jié)

本文只是Flutter UI相關(guān)的冰山一角,并沒有深入了解Flutter的繪制原理。旨在通過源碼了解Widget、Element和RenderObject直接的關(guān)系,理清他們之間的邏輯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容