开篇
上一篇 从源码看flutter(一):Widget篇 咱们了解到了关于 Widget
的相关常识, 知道了 Element
都是经过 Widget
的 createElement()
办法来创立的。
那么,是谁] B U : ^调用了 createElement()
办法?经过查找, 发现只要两处调用了这个办法。分别是:
-
Elem- | v Y & ment
的inflateWidget(...)
办法 -
RenderObjectToWidgetAdapter
的attachToRenderT0 F P / Bree(...)
办法
第一个办法在 Eles E z ? Lment
内部,而且不是 static 办法,明显 Element
不或许随便调用自己的办法创立自己, 所以它是用来生成其他 Element
目标的。而第L F X I ( J一个 Element
便是在第二个办法被创立出来的。
在咱们介绍 Element
目标之前,咱们能够先简略了解一下第一个 Element
的创立过程
RenderObjectToWidgetElement
咱们知道,flb + r g J 4 G {utter的进口在 runApp(widget)
办法里,咱们能够看一下:
runApp(app)
void runApp(Widget app) {
WidgetsFlutterBinding., X f D f v 9 D 1ensureIni( F | ? e }tiali3 M f Z W i m azed()
..scheduleAttachRootWidge+ & n -t(app)
..schedule4 S l t ! _ T *Warmn * ] v ( / y 5 .UpFrame(Y } 3);
}
在这儿进行了一切的初始化操作,而咱们经过 runApp(app6 | e v . Q p Y t)
传入的根 Widget
被第二个Z [ $ & p ,办法 scheduleAtta; U c lchRX a j . c [ l oootWidget(app)
所调用,从这个办法进入
scheduX X EleAttachRootWidW A e [ 8 Uget= r 6 X 8(app)
mixin WidgetsBinding8 0 ~ # ! ( j E on BindingBase,~ x i 3 D { u * ServicesBinding, SchedulerBinding, GesturM Q 0 GeBinding, RendererBiD + S Y ( h # K Xnding, SemanticsBinding {
...
@protectr _ ~ I J X (ed
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
...
void attachRootWidget(Widget rootWie ? D 8 t T udget) {
_renderV( b * BiewElementd S y s v = RenderObjectToWG * %idgetAdapter<RenderBox>(
container: renderView,
debugShot 3 = H z h a ?rtDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderF o 8 kViewElement as Ren0 ? M L O } ]derObjectToWidgetElement<RenderBox>);
}
...} ? H P ` k S Z 8
}
能够看到,最终经过创立 RenderObjectToWidgetAdapter
目标,并调用其 attachToRenderTree(...)
办法创立了 RenderObjectToWidgetElemM w $ Nent
,咱们简略了解一下
attac8 J 8 ] 4 $ 5hToRenderTrK p X Z L 5 * 4ee(…)
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
...
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwne) C ( @ Nr owner, [ RenderObjectToW` F b : jidgetElement<T> element ]) {
...
eles V # K b ? g ument = createElement(n j . ! I Z S);
...
return element;
}
...
}
这儿的 createElement()
也便是咱们之前提到过的,第二个调用的当地。之后5 B r & 8 n T T一切的 Element
都是经过其 父Element 调用 inflateWidget(...)
办法所创立了
接下来,咱们开端正式介绍 Element
目标
Element
咱们常用的 Statef h # g G Y ^fulWidget
、Statele@ Z L + A JssWidgeW - u % - c & zt
所对应的 Element
目标,承继联系如下:
xxxElement -> ComponentElement -> Element
许多其他的 Element
目标也都是直接或许间接承继于 ComponentElement
,不过 RenderObjectWidgn : / } } p w r oeB 4 T 1t
的 Element
承继联系如下:
RenderObjectElement -> Element
下面,咱们从 Element
的结构函数开端
Element(widget)
Elemz [ y L #ent(Widget widget)
: assert(1 E K N 3 g | : owidget != null),
_widget = widget;
在结构函数里边,进行了 Element
所对应的 Widget
目标的赋值。接下来看一看 Element
的结构
abstra. H W j - X a 0 (ct class Element extends DiagnosticableTree implements BuildContext {
...
}
Di| z ^ 9 w kagnosticableTree
在第一篇现已介绍过,这儿不再赘述。能够看到这儿有个咱们了解的目标 BuildContext
, 常常能够在 Widget
或 State
的 build(...)
办t Y A W 3 U s 8法中看到它,咱们先来简略的了o , Z { E d ]解一下它
BuildContext
abstract class B~ ? { 3 ^ Z ) h *uildContext {
Widget get2 ~ 0 widget;
BuildOwner get owner;
...
RenderObject findRenderObject();
.Y @ N ..
InheritedElemen ) o n r S U xt getElementf ? x 1 ? cForInheritedWidgetOfExactType<T extends InheritedWidget>();
T findAncestorWidgetOfExactType<T extends Widget>();
...
void visitChildElements(ElementVisitor visitor);
...
}
上面列出了比较典型的一些办法。BuildContext
是一个抽象类,由于 dart 中没有 Interface ,而这儿的 BuildContext
本质上只提供各种调用办法,所以完全能e B ? / 0 O a r够把它当成 java 中的接口
其间 BuildOwner
目标只在 WidgetsBiJ L 7 f d tnding
的 initInstances()
中初始化过一次,也便是说大局只要唯一的实例。他是 widget framework 的管理类,实际上的的作用有许多,比方在 Element
中,就负责管理它的生命周期
其他的一些办法:
- findRenderObject(): 用于回来当时
Widget
对应的RenderObp b w 9 3 W jject
,假如当时Widget
不是RenderObjectWidget
则从children中寻找 - getElementForInheritedWidgetOfExact` lType(): 在保护的
Map<Type, InheritedElement>
中查找InheritedElement
,在咱们熟知h J : } w F ?的Provider
中的Provider.o* 4 N (f<T>(context)
便是经过这种办法获取数据类的 - findAncestorWidgetOfExactType(): 经过遍历
ElS Q . $ -ement
的 parent 来找到指定( ; 8 i类型Widget - visitChildElements(): 用于遍历 子Element
BuildContext ; ~
大致就介绍这些8 g @ ; o T w,接下来咱们来看 Element
中的一些成员变量
Element的成员变a _ – U量
abstract class Element extends DiagnosticableTree implements BuildContext {
...
Element _par p f E R - +rent;
...
dynamic _slot;
...
int _depth;
...
Widget _widget;
...
BuildOv B X n D 7 jwner _owner;H ) 4 x %
...
bool _active = false;
...
_ElementL_ s H w * t K _ifecycle _debugLifecycleState = _ElementLifecycle.initial;
...
Map<Type, InheritedElement> _inheritedWidgets;
...
bool _dirty = true;
...
bool _inDirtyList = false;
}
上面列举出了首6 Z f * w S x I要的一些成员变量
Element
中默许持有^ x [ ( p k parent
目标,而 slot
用于表明它在 parent
中 child列表 的方位,假如 parent
只要一个 ch8 ~ r & j i +ild , slot
应该为 nA i [ull ,再来看看剩余的一些变量
- depth : 当时
ElemeM z N [ B Snt
节点在树中的深度,深度是递加的,且有必要大于0 - _active: 默许为 false, 当
Element
被增加到树2 R Y F p – x 1 (后,变为 true - _inheritedWidgets: 从
parent
一直传递下来,保护了一切e + f C { M 7InheritedElement
,不过很猎奇为什么这儿不直接用 stax # , Ctic 修饰,是为了方便垃圾回收吗? - dirty: 假如为 true 就表明需求 reBu( ) u 1ild 了, 在
markNeedsBuild()
中会被设为 true - _inDC E U ZirtyList: 当
Element
被标记为dirty
后,随之会将ElemT q g eent
放入BuildOwner
中的_dirtyElements
,并设置为F [ J ^ n d x 1 p true ,等待 reBuild
还有一个 生命周期目标 _debugLifecycleState
enum _E( # 7lementLifecyX ] } ~ H H m xcle {
initial,
active,
inactive,
defunct,
}
它对外部是隐藏的,这个生命周期和 State
的有点相似,不过其间的 active
和 inactive
是能够来回切换的,这儿就涉及到 Eleme& } @ 0 } V @ jnt
的复y / ! I l = e用了,后边会说
然后是 Element
的一些首要办法,& [ ^ – T ;咱们简略的看一下
Element的办法
RenderObr V 4 ) n M |ject get renderObject) e 4 $ x z { ... }
voi} G } Z l ? 9d visiw qtChk o 4ildren(ElementVisitor visitor) { }
@override
void visitChildElements(ElementVisitor visitor) {
...
visitChildren(visitor)K : L _ Y n Y G B;
}
@protected
Element: G Q d b T # R Q updateChild(Element child, Widget newWidget, dynamic newSlot) {9 $ C l V f i ... }
@mustCallSuper
voidK W w @ Y mount(Element parent, dynamic newSlot) { ... }
@mustCallSup( C c 4er
void update(covariant Widgetv 3 . : 7 _ ( f M newWidget) {
...
_widget = newWidget;
}
Element _retakeInactiveElement(GlobalKey key, Widget newWidgeZ B y It7 K v U w Y) { ... }
@protected
Element inf; p X E | @ f 6 )lateWidget(Widge~ A 5 ~ v kt newWidget, dynamic newSlot) { ... }
@protected
vL w # q e $ qoid deactivateChild(Eleme( int child) { ... }
@mustCallSuper
vo; g ] # . t ~ vid activate() { ... }
@mustCallSuper
void deacti* ; Z G F d { ] mvate() { ... }
@mustCallSuper
void unmount() { ... }
@mustCallSuper
void didChangeDependencies() {
...
markNeedsBuiln 9 + R Qd();
}
void markNeedsBuild(){ ... }
void rebuild() { ... }
@protected
void performRebuild();
上面的首要办法中,最中心的是 mouH # | @nt()
、unmount()
、inflo T ^ v L }ateWidget(...)
、updatu S t eChild(...)
、rebuild()
这些M w 4 R H { C I
这儿咱V K 3 ! O h 5们不去直接介绍这些办? @ s } s p法的作用,由于脱离上下文独自看的话或许阅$ Z –览体验不会太好,后边会走一遍 Element
的创立流程,在这个T S j S ` Z B z B过程中去阐述各个办法的作用。
不过咱们能够先看其间一个办法 rE P U ` w G 5 nenderObject
了解一下 Element
与 RenderObject
的对应联系
RenderObject get renderObject {
RenderObject result;
void visit(Element element) {
assert(result == null); // this verifies that there's only oc ~ ~ + v 3ne child
if (element is RenderObjectG + +Element)
result = el| Y a W v - 6 Rement.renderObject;
else
element.visitChildren(visit);
}
visit(this);
return result;
}
解释一下便是,假如当时 element
是 RenderObjectElemenw n , j g n Mt
的话,直接回来它持有的 renderObject
,否则遍历 children 去获取最近的 renderObject
目标
从这儿也能够知道 RenderObject
只与 Rend* s * T . LerObjectElement
是一一对应的,与其他 Element
则是一对多的联系,也验证了咱们上一篇中的判定
不过这儿有一点需求吐槽的是,在办法里边直接界说办法,阅览体验不是特别好,而后边这样的状况还会许多
接下来,咱们预备进入 Element
的创立流程进口
Element 创立流程进口
既然要走创立流程,自然是要找个起点的。在上一篇中,咱~ : T们知道} . v s f经过 createElement()
创立 Element
的办法只在两个当地被调用:
- 其一是作为根节点
Elf } ; * O O { dement
的RenderObjec3 f F + h F h mtToWidgetElement
在RenderObjectToWidgetAdapter
的attachToRenderTree(...)
中被创立 - 另一个是其他一切
Element
在inflateWidget(...)
办法中被创立
咱们以第二个办法为进口,进入 Element
的创立流程,先简略的看一下第二个办法
abstract cD z ! v ( o 1 | |lass Element extends Diagnostica l y { 1 v d GbleTree imZ a & f h z +plements BuildContext {
...
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
.Z j U ~ N 1 o ; 0..
final Elemeq j Int newCd M ( g q r u } (hild = newWidg* t j Tet.createElement~ Q - 0 K * Y();v @ Q v
...
newChild.mount(this, newSlot);
assert(newChild._debugLifP V D T % AecycleState == _ElementLi; 9 xfecycle.active);
return newChild;
}
...
}
能够看到,上面最终调用了 Element
的 mount(...)
办法,所以这个办法算是各个 Element
的进口了。
上一篇咱们提到过,不同 Widgeta - Y
对应 Element
的完成都不相同,其间最广泛的两种完成分别是 ComponentElement
和 RenderObjectElement
。
咱们能够从第一个开端了解
Comp# K T ? m D ponentc e bElement 的创立流程
进入它的 moV a i y 1 Gunt(...)
办法
mount(…)
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
...
_firstBuild();
...
}
调用了父类,也便是 Element
的 mount(...)
Element ->] 4 z ! : _ n ; mount(…)
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
...
_parent = parent;; i Y G i a V }
_slot = newSlot;
_depth = _parents g 2 U { V != null ?7 1 X . j _parent.depth +F a 7 = t 1 : 1;
_active = true;
if (parent != null) // OnlyZ J : H j x ) assign owne* ! h g 4 Jrship if the parent is non-null
_owner = parent.owneQ T u B | m E Cr;
final Key key = widget.key;
if (key is GlobalKey) {
key._registerg H R w b $ v *(this);
}
_updateInheritance();
assert(() {
_debugLifecyn ! D . D 0 ccleState = _ElementLifecycle.8 G T w 5 q m N ;act4 : r 9 Cive;
return true;
}());
}
能够看到,在 mount(...)
中,进行了一些列的初始化操作。
其间假如传入的 key
是 GlobalKey
,会将@ E ] L q ~ ) m D当时 Element
存入 GlobalKey
中保护的 Map<G s 5 s R w d %GlobalKey, Element>
目标。
最终会将生命周期设置为 _ElementLif8 O @ r ~ b L ~ecycle.active
接下来,能够看一下 ComponentElement
的 _firstBuild()
_firstBuo T lild()
void _firstBuild() {
rebuild()Y # :;
}
调用了 rebuild()
,它是在 Element
中完成的
Element -> rebuild()
void rebuildr P E f , q 4 z s() {
...
performRebuilg l * 2 6 p Rd();
...
}
@protected
voiK 6 b N 7 Od performRebuio 2 =ld();
最终调用到 performRebuilQ e l d K |d()
办法,Element
中这个办法什么都没做,便是交由子2 b D x B类去完成的,接下来回到 ComponentElement
performRebuild()
@override
voi@ S 7d perfoT P ~rmRebuild() {
if (!kReleaseMode &+ $ @ Q 1 |amp;& debugProfileBuildsEnaq } X } + o a i ybled)
Timeline.startSync('${widget.runtimeType}', arguments: timelineWhitelistArguments);
...
Widget built;
try {
built = build();
...
} catch (e, stack) {
built = ErrorWidget.builder(...);
} fin+ m pally {
...
_dirty = false;
...
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);Q : & F P , | q m
} catch (e, stack) {
built = ErrorWidget.builder(...);
_child = updD b y I 7 } 4 ateChild(null, built, slot);
}
if (!kReleaseMode &L 1 t X ) s 9amp;& debugProfileBuildsEnabled)
Timel1 u s @ P | % -ine.finish y } e ~ ( 9Sync();j % c f b p
}
能够看到,开头和结尾都做了debug形式的判别,并使用了 Timeline
这个目标,它的作用其实便是咱们之前介绍过的,用于在 DevTool 中检测性能体现
能够看到,上面经过调用咱们最了解的 build()
办法来创立 Widget
,假如发作异常的话,就( j n会在 catc_ 3 h a 7 bh
语句中创立一个 ErrorWidq . { # fget
, 也便w * }是咱们常常遇见的那个红色的错误界面啦!
后边会经过 updateF V K xChild(...)
来给当时 Element
的 _child
赋值
而 updateChild(...)
位于 Element
中
Element -> updateChildS 9 – 2 x W(…)
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
if (newWidget == null) {
if (child != null)
deactivateChild(cT O ? S K ghild);
retub j 9 N Z wrn null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.si v T z V ( L 2lot != newSlot)
updateSlotForChild(child, newSlg + O o | q ! O 3ot);
return child;
}
if (Widn tget.canUpdate(child.widget, newWidget)) {
if (c~ @ Uhild.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
asse2 , lrt(child# y { ? i.widget == newWidget);
assert(() {
child.owner._debugElemen; h ? c f 1 [tWasRebuilt(child);
return true;
}());
return child;
}
deac% 7 n m itivateChild(child);
assert(child._parent == nu) N u j b p Mll);
}
return infl] F ateWidget(newWi2 K M odget, newSlot);
}
updateChild(...)
是非常重要的一个办法,它接受三个参数,分别是 Element child
、Widget newWidget
以及 dynamic newSlot
,传入的参数不同,这个办法的作用也不相同,首要分为下面几种状况:
newWidget为null | newWidget不为null | |
---|---|---|
child为null | ①.s M | B 5 9 3 I回来null | ②.回来新的Elment |
child不为null | ③.移除传入child,回来null | ④.依据 canUpdate(…) 决定回来更新后的child或许新Element |
其间的 deactivateChild(child)
便是将传入 Element
移除去
而咱们k H 4 9 & o K在 performRebuild()
中创来的值是: child为null 而且 newWidget不为null,属于第二种状. K = u况。= ^ @ U 0 K直接进入 inflateWidget(...)
办法
Element -> inf+ c D G X [lateWidget(…)
又回到最初的起点
@protected
Element inflateWidget(WidgeF B # v % S dt newWidget, dynamic newSlot) {
assert(newWidget != null);
final KL X qey key = newb 9 ? a v & YWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debuU : 9 q A O T 1gCheckForCycles(newChild);
return true;
}());
newChild._acc 7 StivateWith` j J 4 / % h 8 EParent(A B 4this, newSlot);
final Element updU j 2 1atedChild = updateChl o ` 3ildM ? & 4(newChild, newWidget, newSlot);
assert(newChiO Y o eld == updatedChild);
return updatedChild;
}
}
//创立新的Element,开端下一轮循环
return newChild;
}
办法后部分的逻辑之前现已说过,这儿能够看一下前部分关于 GlobalKey
的部分。假如获取到 widget
的 key
是 Global! Q g b t / GKey
, 而且之前 Widget
现已在 Element
的 mount(...)
中注册到了 GlobalKey
的话,就会在这儿取出而且复用。这部分是在 _retv ) T { ` . b u HakeInactiveElement(...)
完成的,能够简略看一下5 Q 4 k | a % e P:
Elemei [ Q @ % W ; {nt _retakeInactiveElement(GlobalKey key, Widget newWidg- p z Xet) {
final Element element = key._currentElement;
if (j Y 1 D f 1 E _ Welemen6 : Y =t == null)w . V
return null;
if (!Widget.canUpdate(element.widget, newWidget))
return null;
...
return element;
}
当Element
不存在或许无法更新时,则不会进行复用,回来 null
假如结果不为null,后边{ 8 ) 2 X 5 n再调用 updateChild7 p ? r Q ((...)
办法,这儿传入的参数都不为null– C v 4 ) ) $ A .,所以会进入之前所说的第四种状况:
Elx g ! =ement updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
//这儿一定是true的
ifx Q X ` s ~ s (Wid! s n o e ` C t 2get.canUpdate(child.^ # G 9 bwidget, newWidget)) {
if (child.slot != n5 % 6 a A + n 3ewSlot)
updateSU 2 M f ZlotForChild(child, newSlot);
child.update(newWidget);
assert(ch% - P Kild.widget == newWidget);
assert(() {
child.owner._debugElemen% $ r btWasRebuilt(child);
return true;
}());
return child;
}
..M F U.
return inflateWidget(newWidget, newSI 4 d ( X v %lot);
}
注意到上面的部分,调用 chi$ - )ld
的 update(newWidget)
办法,这个办法除了更新当时 Element
持有的 Widget
外,剩余的逻辑都交给子类去完成了
那么 ComponentElement
的创立流程大致就讲到这儿
下面,咱们能够看一下 ComponentElement
的两个子类^ B ^ StatelessElement
与 StatefulElement
StatelessElement
class StatelessElement exten@ i p b ) = hds ComponentElemv r ! m k ; h Hent {
/// Creates_ E R d & G ? an elu l 0 Kement that uses the given widget asZ 5 b its configurX * q Y ! :ation.
StatelessElement(StatelessWidget widget) : super(widget);
@override
StatelessWidget get widG p @ b ) 6 T cget =>3 D = 3; sup. a *er.widget as Statel3 K } 0 , TessWidgeE e q u A 3 U x vt;
@override
Widget build() => widget.build(this);
@override
void upB X Q K 7 ; cdate(StatelessWidget newWidget) {
supi , 4 ;er.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}
StatelessE@ a T % 1 i %lement
非常简略,重写的 updv ) q . 2 { 5 v ;ate(...)
也仅仅调用了 rebuild()
,感觉没有什么可说的。
接下来看看 Su y X ` i d 8tatefulElement
StatefulElement
class StatefulElement extends ComponentElement {
StatefulEl* D o g 7ement(StatefulWidC h Kget widget)
: _statz * e = widget.createSta! # K 8te(),
super(widget) {
...
_state._el: + # ( $ ; ` Gement = this;
...
_state._widget = widget;
...
}
@override
void _firstBuild() { ... }
@override
void update(State: { ? 1fulWidget newWidget) { ... }
@override
void unmount()_ 5 + : 3 l { ... }
}
这儿展示了 StatefulElement
一些首要的办法,能够看r 8 h到在结构函数中把 Element
和 Widget^ w Y Y A !
目标放入了 State
中
接下来看一下剩余三个办法都做了什么
_fir[ U V n 5 C a YstBuild()
@override
void _firstBuild() {
...
final dynamic debugCheckForReturnedFuture = _state.inib @ Y j d StState() as dynamic;
...
_state._debugLifecy) + YcleState = _StateLifecycle.initialized;
...
_state._ - z ~ YdidChangeDependencies();
...
_state._debugLifeW ^ ` :cyc* + h Q 8 h I v yleState = _StateLij d -fecycle.ready;
.3 U Y . 6 h..M 1 O u L p
super._firstBuild();
}
ComponentElement
中是在 mount(...)
里调用 _firstBv # + Juild()
的,这儿重写了这个办法,而且在里边进行了一些初始化的操作,而且调用了 State
的 initState()
办法
update(…)
@override
void update(Z / M [ _ _ EStatefulWidget newWidget) {
super.u# 6 rpdate(newWidget);
...
final StatefulWidget oldWi9 5 u # d o [ udget = _state._widget;
...
_dirty = true;
_state._widget = widget as StatefulWidget;
...
final dynamic debugCheckForReturnedFutud O P . J z W h cre = _state.didUpdateWidget(oldWidget) as dynamic;
...
rebuild();
}
在这儿首要是调用了 State
的 didUpdateWidget(..r ] X D ) M 8.)
办法,其他内容和 StatelessElement
差不多
那么到了这儿,关于 Statef] U xulWidget
中咱们常用的 setState()
办法| = & f V Y w ` d,它具体会走过 StatelessElement
的哪些过程呢,下面咱们就来看一下
StatefulElement的改写流程
咱们知道 State
的 setState()
办法会调用 Element
的 markNeedsBuild()
Element -> markNeedsBuild()
BuildOwner get owC # u G I Y Fner => _owner;
void markNeedsBuild() {
...
if (d4 t l ~ h ) mirty)
return;
_dirty = true;
owner.scheduleBuildFor(thi) 7 ms);
}
下面进入 BuildOwner
看看 schedu: k F nleBuildFor(element)
做了些什么
BuildOwner -> scheduleBuildFor(element)
final List<Element>R ; 2 V P; _dirtyElements = &l) = & S I d : !t;Element>[];
void scheduleBuildFor(Element element) {
...
if (element._inDirtyList) {
...
_dirtyElementsNeedsResorting = true;
return;
}
...
if (!_scheduledFlushDirtyElements &&: ) T * x Xamp;} [ 8 z 6 { onBuildScheduled != null) {
_scheduledF2 e ) E + IlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements= 2 - U | ; ^.add(element);
element._inDirtyList = true;
...
}
能够看到,scheduleBuildFor(element)
后边w v = ^ / $ ; T会将需求改写的 Element
增加到 _di& J C | R d e h 0rtyElements
中,并将该 Element
的 _inDirtyList
标记为 t U 9rue
但之后W & n P并没( d c *有做其他的操作,那改写到底是如何进行的呢?这就要看前面调用的一个办法 onBuildScheduled()
了
BuildOwner -> onBuildScheduled()
这个办法是在 BuildOwner
被创立时设置的
mixin WidgetsBinding on BindingBase, ServiceH A Y L a Q 4 V 8sBinding, SchedulerBib _ ynding, GestureBinding, RendererBindint = c % } - bg, SemanticsBinding {
...
@o( Q E - 9verride
voio } 1 ` Y + P | [d initInstances() {
...
_buildOwner = BI V KuildOwner();
buildOwner.onBuildScheduled = _handleBuildSco T g Cheduled;
...
}
...
}
来看一下 _handleBuildScheduled
Widge= I z b N | –tsBinding -> _g s 6 ) G 6handleBuildScs : q Iheduled
void _handleBuildScheduled() {
...
ensureVisualUpdate();
}
ensureVisualUpdate()
在 SchedulerBinding
中被界说
mixin SchedulerBinding on BindingBase, ServicesBinding {
...
void ensureVisualUpdate() {
switch (schedulerPhase) {
...
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
...
}
}
...
}
后边会进入 scheduleFrame()
办法
SchedulerBinding -> scheN N V u [duleFrame()
@proteb N Rcted
void ensureFrameCallb/ c _ b v 0 e ]acksRegistered() {
window3 8 m o A %.o% ~ j o ynBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDe | M J )rawFrame;
}
void scheduleFrame() {
...
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame ={ f R Q true;
}
这儿会调用 window.scheduleFrame()
Window –n Y 1> scheduleFramez A E()
class Window {
...
/// Requests that, at the next appropriate opportunity, the [onBeginFrame]
/// and [onDrawFrame] callbacks be invoked.T / V - [ k s ^
///
/// See also:
///
/// * [S : ; l 0chedulerBinding], the Fluttew ; y ? = & ( 3 `r framework class which manages thex i h r S
/// scheduling of frames.
void scheduleFrame() native 'Window_scheduleFrame';
}
到了这儿,便是对 engine 的相关操作了。其间,经过各式各样的操作之后G ] W y _ ^ f a,会回调到 dart 层的 _drawFrame()
办F ; – b l法
sky_engine -> ui -> hooks.dart -> _drawFrame()
假如你对 engine 中的这一部分操作感兴趣的话,能够看一下这一篇文章 Fd T 7 * p 3 Tlutter渲染机制—UI线程, 由于都是C++的内容,逾越了我的才能领域,所以这儿直接去看大神的吧
_drawFrame()
办法内容如下:
void _drawFrame() {
_invoke(window.onDrawFrame, window._onDrawFra9 G c b { , p meZol b b 1 g dne);
}
最终,会调用到之前在 SchedulerBinding
中 onDrawFrame
所注册的 _I Q G , ) {handleDrawFrame
办法, 它会调用 handleDrawFrame()
SchedulerBinding ->x J q . i v handleDrawFra3 ^ ome()
void handleDrawFrame() {
...
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPha1 Y q p G Jse.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_i5 ~ v ` n W b n ynvokeFrameCallback(callbaN F Kck, _currentl M / & GFrameTimeStamp);
...
}
...
}
在这y : _ _儿会遍历 _persistentCas P N n K .llbacks
来履行对应办G % ; 5 t a e Q &法,它是经过 RendererBinding
的 addPersistentFrameCallback
增加,而且之后的每一次 framg F – n 4 . I ye 回调都会遍历履行一次
这儿即将履行的办法,是在 RendererBinding
的 initInstances()
中增加的 _handleS * S kPersistentFrameCallback
void _hanB s s f 0 0 KdlePersistentFrameCallback(Durationa W S x | n 2 ) timeStamp) {
drawFrame();
_mouseTrackew 8 | S = 3 m yr.schedulePostFrameCheck();
}
最终,会调用到 WidgetBinding
的 drawFrame()
WidgetBinding -> drawFrame()
@override
void drawFrame() {
...
try {
if (renderViewElement != null)
bx [ } 7 T z ! v duildOwner.buildScope(renderViewElemW V + D . , ] : dent);
super.drawFrN 5 -am` C I +e();
buildOwner.finalizeTree();
}
...
}
renderV* N $ Y @ :iewElement
便是咱们之前看 runApp()
流程中所创立的 根Element
最终调= ! r h s又回到了 BuildOwner
目标,并调用它的 buildScope(...)
办法
BuildOwner -> buildScope(…)
buildScope(...)
用于对 Element Tree 进行部分更新
void buildScope(Element context, [ VoidCallback callback ]) {
...
_debugBuilding = true;
...
_scheduledFlushDirtyEleme, D H Y 5 ^ Onts = true;
...
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyEleme] V 9 b v s znts.lengtho % L : X A;
int index = 0;
while (index < dD w + 1irtyCount) {
...
_dirtyElements[index].rebuild();
...f 0 ) c S F _ 4 )
index += 1;
...
//假如在@ $ d _ @ L `部分更新的过程中,_dirtyElements发作了变化
//比方8 V w y U或许有新的目标插入了_dirtyElements,就在这儿进f k *行处理
}
...
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElementsa % r n q x g N.cle2 ; F M v [ar();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
...
_debugB* n : wuildinw v g = false;
...
}
所以其实从上面咱们就能知道,部分更新的原理便是把需求更新的目标W P } s I存入了 _dirtyElementU w c r = ns
中,然后在需求更新的时分,遍历它们,进行 reBuild()
遍历之前,会调用 sort(...)
办法k 2 6进行排序,判别条件是 ElX 0 ( Q x P B f 1ement
的深度,依照从小到大排列,也便是关于 Element
是从上往下更新的。
更新完毕后,_dirtyElements
会被清空,各个标志位也会被重置
到这儿,StatefulElement
的改写流程咱们现已了解到了,接下来咱们了解一下它的毁掉流程,一起趁便也能x n Z G T , p h够Z ^ @ ^知道 Element
的毁掉流程
StatefulElement的毁掉流程
其实在 drawFrame()
的 buildScope(...)
办i P V 9 _法后紧跟着的 buildOwner.finalizeTree()
便是用于进行G * = Q ] @ , $ + Element
的毁掉的
不过这儿不将它作为进口,记住咱们之前 Element
的 updateChild(...)
办法里边,& N L q 9 $ V有两处会对 Element
进行毁掉,3 K X而调用毁掉的办法便是 deactivateChild(child)
下面就从这儿作为进口
Element -> deactivateChild(child)
@protected
void deactivateChilZ 5 x ! Bd(Element child) {
...
child._parent = null;
child.detachRenderObject();
owner._inactiveElements@ 7 a v q 8 [ X 3.add(child);
...
}
在这儿会清除去 child
关于 parentF j ~ r D 0 .
的引证,一起也调用 detachRend A q X 3 derObject()
去毁掉 RenderObject
,关于它的细节下一片蘸6 p @ = e再说。
最首要的? v P p M – & J `还是向 BuildOwner
的 _inactiveElements
中增加了当时要毁掉的 child
咱们能够先了解一下 _inactiveElements
_InactiveElements
class _I # 2nactiveElements {
bool _locked = false;
final Set&` 0 9lt;Ey I 1lement> _elements = H) e h n ~ @ . _ashSet<Element>Q j Q();
static void _deactivateRecursively(Element el+ ? a . t kement)) g A + D {
...
element.deactivate();
...
element.visitChildren(_deactivateRecursively);
...
}
void add(Element element) {
...
if (element._active)
_deactivateRecursively(element);
_elements.add(d R W T 6 v 6 3element);
}
}
能够看到,_InactiveE# ` / o 4 Wlements
中使用 Set
存放了一切需求毁掉的目标
在 add(element)
办法中,假如当时要毁掉的目标还处于活跃状况,则经过递归的办法,遍历它的 children
,对每个 child Element
调用 deactivate()
来设为毁掉状况,能够看一下 deactivate()
办法
///Element
@mustCallSuE S T i / ) J q {per
void deacti& 9 N | - zvate() {
...
_inhe, j p & i a @ritedWidgets = null;
_active = false;
.5 ] u ] b..
_debugL l .ifecyA E b TcleState = _ElementLifecycle.inact= Z J fiv~ | ; n ; Dep = u;
...
}
即将被毁掉的 Element
生命周期会变为 inactive
这儿咱们收集完要毁掉的 Element
之后呢,就会在Widget6 K e O 6 SsBinding
的 drawFrab A ] . me()
被H i V – F 9 k触发时调用0 3 % 0 n a t g g finalizeTree()
来进行真实的毁J l 2 g s * 9掉了
BuildOwner -> finalizeTree()
void finalizeTrea S *e() {t ) a p
...
lockState(() {
_inactiveElements._un* P n ]mountAll(); // this unregisters the GlobalKeys
});
...
}
这儿调用了 _Inactivs h b 4 B ^ 6eElements
的 _unmountAll()
来进行毁掉
_InactiveElements -> _unmountAll()
voidS ) D - - y E 3 6 _unmountAll() {
_lockeT - ~ ) a 3 D i _d = true;
final List<Element> elements = _elements.toList()..sv 5 ~ x U c +ort(Element._sort^ G p ] e 1 C $ d);
_elements.cleari P g();
try {Q p ! S N 1 S ]
elements.reX ^ o % & C / o qversed.forEach(_unmount);
} finally {
asseh 9 X nrt(_elements.isEmpty);
_locked = false;
}
}
这儿的毁掉也是由上到下的,调用了 _unmount(element)
办法
void _unmount(Element el. p D c f s # bement) {
...
element.visitChildren((Element child) {
assert(child._parent == element);
_unmount(child);
});
element.unmount();P 7 c p V k
...
}
不得不说,dart 将办法作为参数传递,而且在某些状况下能够省掉输入办法的参数真是好用,不过或许就牺牲了一点 ] f x可读性
_unmount(element)
也是遍历 children ,然后调用 child Element
的 unmount()
来进行毁掉
在 _InactiveElements
中还有一个 remove
办法,介绍 unmount()
前咱们先把这个给看了
_InactiveElements -> remove()
voidF J / ~ H remove(Element element) {
...
_elements.remove(element);
...
}
这儿便是从 Set4 ; }
中移除传入的 Element
目标,在之前的 _unmountAll()
中会经 u 3 W p过 clear()
清楚 SeX 2 ` | Ct
内一切的元素,那为什么这儿会有这样一种4 x s状况呢?之前咱们在 Element
的 inflateWidget(...)
中提到过,GlobalKey
是能够用于复用 Element
的,被复用的% 3 $ Element
目标无需重新创立。咱们再来看一下
Element -> _retakeInactiveElement(.– , H d ? 7 l U..)
@protected
EM B h Q c T 5 H Jlement ins P 5 ) k J { z wfla- y U V [ N n ~teWidget(Widg^ 8 Q I Fet newWidget, dynamic newSlot) {
...
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retake& D a T Z , b O !InactiveElement(keyZ G d y * G L j b, newWidget);
if (newChildi - L != null) {9 8 8 ( V 1 G 6 h
...
n- e S M l DewChild._activateWithPH P Karz , l 4 e n xent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot)q ! 2 . ] !;
asseP j / M ! a -rt(newChild == updatedChild);
return updatedChild;
}
}
...
}
Element _retakeInactiva & C d L 5eElemC N Z uent(GlobalKey key, Widg# )et newWidget) {
...
final Elh f ] Gement element! z ] m d # v 6 = key._currentElement;
if (element == null)
return null;
if (!W7 Z Y Zidget.canUpdate(elemenQ 6 t 8 D ] ct.wV t widget, newWidget))
retI | g + 9 @ , F |urn null;
...
final Element parent = eT ? $lement._parent;
if (parent !V . r { Q F a= null) {
...
parent.forgetChild(element);
parent.deactivateChild(element);
}
...
owner._inactiveElements.remove(elemen r x Q K v z n Hnt);
return element;
}6 n #
能够看到,在 _retakeInactiveE( ~ 1 C } ; ` Llement(...)
末尾处,会将被复用了的 Element
从 _inactiveElements
中移除去。获取到这个 E( N G a Plement
后,会调{ p ~ P Z , V –用 _activateWithParent(...)
办法再8 6 6 Y s T次将 Element
激活
Element -> _activateWithParent(…)
void _activateWithParent(Element parent, dynu V / Samic newSlot) {
...
_parent = parent;
...
_updateDepth(_parent.depth);
_activateRecursiM | | 1 & w p z mvel g 7 [y(this);
attachRR W , | Y K 6 R SenderObject(newSlot);
...
}
static void _activateRecursively(Element element) {
...
elW ` Y d h _ wement.al ! o - k 3ctivate();
...
element.visitC3 - 4hildren(_activateRecursively);
}
这儿会经过递归调用,激活 Element
和它的 children
,来看一下 active()
办法
Element -> activate()
@mustCallSuper
void activatL Z j A v He() {
...
final bool hadDependencies = (_dependencies != null && _dependenZ $ {cies.isNotEmpty) || _hadUnsatisfiedDependencies;
_active = true;
...K C 3
_depeM ! _ w a =ndencies?.[ & q . a Eclear();
_hadUnsatisfiedDependencies = falsG | F L O *e;
_updateInherit# G dance();
...
_debugLifecycleStatc I 5 ; _ 7 ! 3 Te = _ElementLifecycle.active;
...
if (_dirty)
oy { . Bwner.scheduleBuildFor(this);
iW + S } J Zf (hadDependencies)
didChangeDependencies();
}
///St- p V ]atefulElement
@override
void ac0 G Q S @ Ctivate() {
super.activate();
...
markNeedsBuild();
}
在 acb a Q *tivate()
中,Element
的生命周期再次变为了 active
, 这也便是咱们之前所说过的, Element
的四个生命周期中, U W f 4 @ 7或许会呈现 active
和 inactive? j J [ n /
切换的状况。
那么在哪种状况下会触发这种复用呢?其实很简略:当W ` ; : k 2 1 } G处于不同深度的同一类型 Widget
使用了同一个 GlobalKey
就能够, 比方下面这样:
Center2 + ? + D U z(
child: changed
? Container(
child: Text('aaa! } 9 , M x ) Wa', key: globalKey),
padding: EdgeInsets.all(20))
: Text('bbb'c 3 !, key: globalKey),
)
当经过 setState
修改 changed
时,就能3 I –够触发复用
插曲就说到这儿,咱0 b F , m 0 ! J G们继续之前的 unmount()
办法
Element -> unmount()
@mustCallSuper
void unmount() {
...
final Key key = widget.key;
ia H S ^ % h $ Yf (key is GlobalKey) {
key._unregister(this);
}& w , ;
assert(() {
_debugLifecycleState = _ElementLifecycle.defunct;
retur7 , M 2n true;
}());
}
能够看到. O 3 6,ue w h T r M 7 Y Xnmount()
中,假如当时C 4 f 4 o 0 M ] Element
注册了 Globalkey
就会被清空掉,一起生命周期会被设置为 defu@ y Rnct
,在 StatefulElement
中会重写这个办法
StatefulElement -> unmount()
@override
void unmount() {
super.unmount();
_state.dispose();
...
_state._element = null;
_state = null;
}
在这儿调用了 State
的 dispose()
办法,而且在之后清理了 State
中持有的 Element
引证,最终将 State
置空
到了这儿,StatefulElement
的毁掉流程也完毕了,本篇文章也接近尾声
当然,还有 RenderObjectE) j 5 g 8 h m _lement
都还没有做过剖析,由于一切和 Renderb / iObject
有关的内容都将放到第三篇来讲,这儿就先越过吧
总结
-
Element
是真实的数据持有者,而State
也是在它的结构函数里被创立,它的生命周期比State
略长。 - 每次改写时,
Widget
都会被; 2 X K , a N r重新创立,而在Element
的创立流程完毕后,Element
只要在canUpdate(...)
回来 false 时才会重新创立,不然一般都是调用它的update(...)
进行更新。StatelessElement
也是这样的。 -
GlobalKey
除了能够跨Widget
传h r . m M ; .递数据外,还能够对Element
进行复用
剩余的总结,就看图片吧
注:以上源码剖析基于flutter stable 1c + 5 J O M J i.13.6
新的版本6 j K ; :或许存在代码不一致的当地,比方updateChild(…),可是逻辑都是相同的,能够定心食用