Flutter渲染 Widget Element RenderObject概述(一)

注意:為了讓分析更加簡(jiǎn)單,和邏輯清晰,我們?nèi)サ袅?strong>部分源碼和注釋,只留下了主要的代碼和邏輯。

最近一直在研究Flutter的渲染問(wèn)題,在深入探索之后發(fā)現(xiàn)總是繞不過(guò)三個(gè)對(duì)象分別是Widget,Element,RenderObject,那么Flutter為什么需要這三個(gè)對(duì)象,這個(gè)三個(gè)對(duì)象是什么關(guān)系?有這三個(gè)對(duì)象會(huì)提高渲染效率嗎?等等這樣的問(wèn)題,我將在接下來(lái)的幾篇文章中為大家找答案。

Widget概述

widget定義

先給出Widget的定義,可能和你之前理解的組件有一點(diǎn)區(qū)別,下面是Flutter對(duì)Widget的定義。

Describes the configuration for an [Element].

翻譯過(guò)來(lái)的大概意思就是,"對(duì)一個(gè)Element配置的描述"。

這個(gè)概念上透露了兩點(diǎn)細(xì)節(jié),第一是,Widget是Element的配置描述,有人一定會(huì)問(wèn),Element是什么呢?在下面的章節(jié)中我們將詳細(xì)介紹Element。第二是,Widget只是一個(gè)配置描述,不是真正的渲染對(duì)象,這里可能有點(diǎn)繞。舉個(gè)例子,看大家能不能理解,Widget就好比是Android開(kāi)發(fā)中的xml,只是描述了一些View的顏色,大小等,真正在屏幕上顯示的是View。

在Flutter中,一切都是組件。在移動(dòng)端開(kāi)發(fā)中組件的概念很常見(jiàn),比如Android的四大組件,F(xiàn)lutter把組件的概念發(fā)揮到了極致,在Flutter中,手勢(shì)(GestureDetector)都是組件,下面是GestureDetector的源碼。


class GestureDetector extends StatelessWidget {

}

abstract class StatelessWidget extends Widget {

}

GestureDetector繼承自StatelessWidget,StatelessWidget是沒(méi)有狀態(tài)的組件,這個(gè)類(lèi)繼承Widget,可以看出來(lái)GestureDetector也是Widget,接下來(lái)我們簡(jiǎn)單分析一下Widget,下面是Widget的源碼。


abstract class Widget {

  const Widget({ this.key });

  final Key key;



  @protected

  Element createElement();//注釋1



  static bool canUpdate(Widget oldWidget, Widget newWidget) {//注釋2

  return oldWidget.runtimeType == newWidget.runtimeType

        && oldWidget.key == newWidget.key;

  }

}

原來(lái)Widget是一個(gè)抽象類(lèi),這個(gè)類(lèi)有一個(gè)構(gòu)造函數(shù),參數(shù)是一個(gè)key,這個(gè)key有一個(gè)很重要的功能,用途是比較兩個(gè)Widget是不是同一個(gè)Widget,在注釋2就用到了這個(gè)key。然后有兩個(gè)方法,分別是createElement()和一個(gè)靜態(tài)的方法canUpdate()。

  • 注釋1

createElement()是一個(gè)抽象方法,子類(lèi)必須實(shí)現(xiàn)這個(gè)方法,但是大部分我們用都是系統(tǒng)的Widget,比如StatefulWidget和StatelessWidget,他們都默認(rèn)實(shí)現(xiàn)了這方法,這方法也非常簡(jiǎn)單,創(chuàng)建了一個(gè)Element。這里面隱含了一很重要問(wèn)題的答案,開(kāi)篇我們問(wèn)了這樣的問(wèn)題,這個(gè)三個(gè)對(duì)象是什么關(guān)系?現(xiàn)在我們至少知道了Widget和Element的關(guān)系了,一個(gè)Widget有一個(gè)Element對(duì)象,是通過(guò)createElement()創(chuàng)建的

  • 注釋2

canUpdate()方法很簡(jiǎn)單,就是判斷oldWidget和newWidget是不是同一個(gè)Widget,如果他們的runtimeType和key相同,就認(rèn)為是同一個(gè)Widget。

Widget的特性

Widget是一個(gè)很重要的概念,但是Widget有一個(gè)更重重要的特性,就是Widget是immutable(不可變的)的,這是什么意思?下面我們講解一下,我們拿Opacity為例給大家講解,講解之前我們先看一下Opacity的繼承關(guān)系。(在講源碼之前我們先看一下Opacity的職責(zé)是什么,Opacity是一個(gè)能讓他的孩子透明的組件,很簡(jiǎn)單也很容易理解。)


class Opacity extends SingleChildRenderObjectWidget {

}

abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {

}

abstract class RenderObjectWidget extends Widget {

}

image

從上面可以看出來(lái),Opacity繼承自SingleChildRenderObjectWidget,這類(lèi)只包含了一個(gè)child的Widget,它繼承自RenderObjectWidget,RenderObjectWidget繼承自Widget。下面是具體分析一下Opacity,下面是是源碼。


class Opacity extends SingleChildRenderObjectWidget {



  const Opacity({

    Key key,

    @required this.opacity,

    Widget child,

  }) : super(key: key, child: child);



  final double opacity;//注釋1



  @override

  RenderOpacity createRenderObject(BuildContext context) {//注釋2

    return RenderOpacity(

      opacity: opacity

    );

  }

  @override

  void updateRenderObject(BuildContext context, RenderOpacity renderObject) {

    renderObject

      ..opacity = opacity

  }

}

  • 注釋1

在注釋1處聲明了一個(gè)屬性,這屬性是final,也就除了構(gòu)造函數(shù)能給這個(gè)屬性賦值之外,沒(méi)有其他的辦法讓這個(gè)值進(jìn)行改變。那我們想改變這個(gè)值怎么辦,唯一的辦法就是創(chuàng)建一個(gè)新的Opacity。

為什么這樣設(shè)計(jì)呢?先透露一下這是Flutter的核心設(shè)計(jì)哲學(xué),在接下來(lái)的章節(jié)中我們將詳細(xì)為大家講解。

總結(jié)

Widget好像是Android得一個(gè)xml配置文件,不參與真正的渲染,只是告訴渲染層我長(zhǎng)什么樣式,并且這個(gè)對(duì)象的屬性是不可以改變的,要想改變只能重現(xiàn)創(chuàng)建一個(gè)對(duì)象。

Element概述

Element定義

還是老規(guī)矩,先看一下定義。

An instantiation of a [Widget] at a particular location in the tree.

翻譯過(guò)來(lái)的大概意思就是,"在Element表示一個(gè)Widget樹(shù)中特定位置的實(shí)例",下面我們看一下Element類(lèi)的源代碼。


abstract class Element extends DiagnosticableTree implements BuildContext {

  Element(Widget widget)

    : _widget = widget;//注釋1



  @mustCallSuper

  void mount(Element parent, dynamic newSlot) {//注釋2

  }



  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {

  }

}

上面找了2個(gè)重點(diǎn)的方法和1個(gè)重要的屬性,其實(shí)Element的屬性和方法非常多,通過(guò)構(gòu)造函數(shù)可以看出來(lái),一個(gè)Element持有一個(gè)Widget,下面我們分析一下Element的創(chuàng)建過(guò)程。

Element創(chuàng)建

通過(guò)上面的Widget概述那一節(jié)我們知道,Widget有一個(gè)抽象方法createElement(),用來(lái)創(chuàng)建Element的,這個(gè)方法的具體實(shí)現(xiàn)有很多,我們找一個(gè)上面我們分析過(guò)的SingleChildRenderObjectWidget,這個(gè)類(lèi)非常簡(jiǎn)單,只有一個(gè)child,下面看一這個(gè)類(lèi)的源碼。


abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {

  const SingleChildRenderObjectWidget({ Key key, this.child }) : super(key: key);

  final Widget child;

  @override

  SingleChildRenderObjectElement createElement()//注釋1

    => SingleChildRenderObjectElement(this);

}

上面已經(jīng)說(shuō)過(guò)這個(gè)類(lèi)的繼承關(guān)系,這個(gè)類(lèi)繼承RenderObjectWidget,構(gòu)造函數(shù)也很簡(jiǎn)單,傳入一個(gè)child,重要的是在注釋1處,這個(gè)類(lèi)創(chuàng)建一個(gè)類(lèi),是SingleChildRenderObjectElement,通過(guò)名字猜想,這一定是一個(gè)Element了,下面我們就分析一下SingleChildRenderObjectElement類(lèi)。

這里驗(yàn)證了,Weight和Element的關(guān)系,一個(gè)Widget有一個(gè)Element對(duì)象,是通過(guò)createElement()創(chuàng)建的。

還在分析之前,我們先看一下SingleChildRenderObjectElement的繼承關(guān)系,下面是SingleChildRenderObjectElement的繼承關(guān)系。


class SingleChildRenderObjectElement extends RenderObjectElement {

}

abstract class RenderObjectElement extends Element {

}

image

從上面的繼承關(guān)系可以看出來(lái),SingleChildRenderObjectElement繼承RenderObjectElement,而RenderObjectElement是一個(gè)Element,大家是不是發(fā)現(xiàn)這繼承關(guān)系和SingleChildRenderObjectWidget非常像,下面是SingleChildRenderObjectElement的源碼。

mount方法調(diào)用


class SingleChildRenderObjectElement extends RenderObjectElement {

  SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);

  @override

  void mount(Element parent, dynamic newSlot) {

    super.mount(parent, newSlot);//注釋2

    _child = updateChild(_child, widget.child, null);//注釋1

  }

}

構(gòu)造函數(shù)比較簡(jiǎn)單,下面我們看一下看,mount方法(到這里有的人一定會(huì)問(wèn)了,為什么上來(lái)就分析mount方法呢?等下一篇文章,我們分析一下Flutter的啟動(dòng)過(guò)程就清楚了),這個(gè)方法是當(dāng)新創(chuàng)建的元素第一次添加到樹(shù)中時(shí),框架會(huì)調(diào)用此函數(shù)。

  • 注釋1

    這方法非常關(guān)鍵,我們將用一個(gè)小節(jié)專(zhuān)門(mén)去分析,請(qǐng)查看updateChild分析小節(jié)。

  • 注釋2

    注釋2處,調(diào)用了父類(lèi)的mount,我們看一下父類(lèi)的mount的方法。


abstract class RenderObjectElement extends Element {

  RenderObjectElement(RenderObjectWidget widget) : super(widget);



  RenderObject _renderObject;//注釋1

  @override

  void mount(Element parent, dynamic newSlot) {

    super.mount(parent, newSlot);

    _renderObject = widget.createRenderObject(this);//注釋2

    attachRenderObject(newSlot);

    _dirty = false;

  }

}

  • 注釋1

在注釋1處,我們發(fā)現(xiàn)RenderObjectElement還持有一個(gè)對(duì)象,這對(duì)象是RenderObject,我們好像明白了一點(diǎn)什么,Element分別持有Widget和RenderObject,到這里我們解答了這個(gè)三個(gè)對(duì)象是什么關(guān)系?的問(wèn)題,一個(gè)Element包含一個(gè)RenderObject和一個(gè)Widget。

  • 注釋2

重點(diǎn)在注釋2的地方,這里創(chuàng)建了一個(gè)RenderObject,調(diào)用的是Widget的createRenderObject方法,下面我們看一下attachRenderObject這方法,下面是attachRenderObject的源碼。


abstract class RenderObjectElement extends Element {

  @override

    void attachRenderObject(dynamic newSlot) {

      _slot = newSlot;

      _ancestorRenderObjectElement = _findAncestorRenderObjectElement();//注釋1

      _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);

      if (parentDataElement != null)

      _updateParentData(parentDataElement.widget);//注釋2

    }

}

  • 注釋1

注釋1正如他的名字是一樣的,找到了Element樹(shù)上的祖先Element,如果祖先不為空,就調(diào)用insertChildRenderObject方法,這個(gè)方法的意思就是把renderObject的child替換成newSlot。

  • 注釋2

用于更新布局?jǐn)?shù)據(jù)的一些信息,這些信息對(duì)于后面的布局至關(guān)重要。

總結(jié)
image
  1. 當(dāng)SingleChildRenderObjectElement被SingleChildRenderObjectWidget創(chuàng)建成功之后,系統(tǒng)會(huì)調(diào)用SingleChildRenderObjectElement的mount(),這個(gè)方法首先調(diào)用super.mount(),也就是上圖的第一步。

  2. RenderObjectElement的mount()先創(chuàng)建了一個(gè)RenderObject對(duì)象,也就是第二步,創(chuàng)建這個(gè)對(duì)象是在Widget類(lèi)中創(chuàng)建的。

  3. 第三步,就是把這個(gè)將RenderObject添加到指定的位置的渲染樹(shù)中。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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