背景
Flutter的第一行程序开始, Dart的main方法中通过调用runApp方法把开发者自己编写的Widget传递进去, 编译运行后, 得到开发者预期的效果。我们很好奇, 这背后经历了什么?或者说, 你经常听到的Flutter三棵树核心机制是什么?接下来, 我们一起探讨。
Flutter程序入口
开发者自己编写的Flutter App一般入口都是在main方法, 其内部通过调用runApp方法将我们整个应用的Widget添加并运行。runApp的方法实现如下:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
上面三行代码代表Flutter启动的核心三步(级联运算符调用)
- WidgetsFlutterBinding初始化(ensureInitialized)
- 调度、绑定根节点, 创建核心三棵树(scheduleAttachRootWidget(app))
- 调度、绘制热身帧(scheduleWarmUpFrame())
WidgetsFlutterBinding实例及初始化
源码如下:
This is the glue that binds the framework to the Flutter engine.
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
分析源码, 可以发现。这个类上面的一行代码: This is the glue that binds the framework to the Flutter engine. 意思是WidgetsFlutterBinding类是Widget框架和Flutter engine的桥梁。 也是整个Flutter的应用层核心。WidgetsFlutterBinding继承自BindingBase, 并且with了大量的mixin类。通过 ensureInitialized()
方法我们可以得到一个全局单例的 WidgetsFlutterBinding 实例,且 mixin 的一堆 XxxBinding 也被实例化。
BindingBase 抽象类的构造方法中会调用initInstances()
方法,而各种 mixin 的 XxxBinding 实例化重点也都在各自的initInstances()
方法中,每个 XxxBinding 的职责不同,如下:
-
WidgetsFlutterBinding: Widget框架和Flutter engine的桥梁核心桥梁主体,Flutter app 全局唯一。
-
BindingBase: 绑定服务抽象类。
-
GestureBinding: 绑定手势事件。处理屏幕事件分发及事件回调处理, 初始化方法中重点是把事件处理回调
_handlePointerDataPacket
函数赋值给window的属性, 以便window收到屏幕事件后调用, window实例是Framework层与Engine层处理屏幕事件的桥梁。void initInstances() { super.initInstances(); _instance = this; window.onPointerDataPacket = _handlePointerDataPacket; }
-
SchedulerBinding: Flutter绘制调度器相关绑定类, debug编译模式时统计绘制流程时长等操作。
-
ServicesBinding: Flutter系统平台消息监听绑定类。Platform与Flutter层通信相关服务. 同时注册监听了应用的生命周期。
-
PaintingBinding: Flutter绘制预热缓存等绑定类。重要的是, 绑定了图片缓存。
void initInstances() { super.initInstances(); _instance = this; _imageCache = createImageCache(); shaderWarmUp?.execute(); }
-
SemanticsBinding: 语义树和Flutter引擎之间的粘合剂绑定类。
-
RendererBinding: 渲染树和Flutter引擎之间的粘合剂绑定类, 重点是持有了渲染树的根节点。
RenderView 就是 RenderObject 渲染树的根节点。
void initRenderView() { assert(!_debugIsRenderViewInitialized); assert(() { _debugIsRenderViewInitialized = true; return true; }()); renderView = RenderView(configuration: createViewConfiguration(), window: window); renderView.prepareInitialFrame(); }
-
WidgetsBinding: Widget树和Flutter引擎之间的粘合剂绑定类。
从上面Flutter的runApp启动方法中, 可以看出, 这些XxxBinding承担的角色大致是一个桥梁关联绑定, 如下:
因为我们主要探讨Flutter启动主流程机制, 以及三棵树的诞生流程, 所以我们需要重点关注RendererBinding 和 WidgetsBinding 类的initInstances()
方法, 如下:
## RendererBinding类
/// The glue between the render tree and the Flutter engine.
渲染树(render tree) 和 Flutter engine之间的桥梁
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
/*
创建管理rendering渲染管道的类, 提供接口调用来触发渲染
*/
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
/*
window变化相关的回调监听
*/
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
/*
创建RenderView对象, 也就是RenderObject渲染树的根节点
*/
initRenderView();
......
}
/// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the next frame is requested.
///
/// Called automatically when the binding is created.
创建渲染树的根节点对象
void initRenderView() {
......
/*
创建渲染树的根节点对象
*/
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
/*
定义renderView的get方法,获取自_pipelineOwner.rootNode
*/
RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
/*
定义renderView的set方法,上面initRenderView()中实例化赋值就等于给_pipelineOwner.rootNode也进行了赋值操作。
*/
set renderView(RenderView value) {
assert(value != null);
_pipelineOwner.rootNode = value;
}
## WidgetsBinding类
/// The glue between the widgets layer and the Flutter engine.
widgets 和 Flutter engine之间的桥梁
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
/*
创建一个管理Widgets的类对象
BuildOwner类用来跟踪哪些Widget需要重建,并处理用于Widget树的其他任务,例如管理不活跃的Widget等,调试模式触发重建等。
*/
_buildOwner = BuildOwner();
/*
回调方法赋值,当第一个可构建元素被标记为脏时调用。
*/
buildOwner!.onBuildScheduled = _handleBuildScheduled;
/*
回调方法赋值,当本地配置变化或者AccessibilityFeatures变化时调用。
*/
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
......
}
分析上面的初始化过程, 以及重点关注的RendererBinding、WidgetsBinding
初始化方法。我们可以知道RendererBinding 中的 RenderView 就是 RenderObject 渲染树的根节点。我们可以得到以下流程图:
通过 scheduleAttachRootWidget 创建关联三棵核心树
WidgetsFlutterBinding 实例化单例初始化之后先调用了scheduleAttachRootWidget(app)
方法,这个方法位于 mixin 的 WidgetsBinding 类中,本质是异步执行了attachRootWidget(rootWidget)
方法,这个方法完成了 Flutter Widget 到 Element 到 RenderObject 的整个关联过程。
/// The glue between the widgets layer and the Flutter engine.
/// WidgetsBinding类
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
/*
Schedules a [Timer] for attaching the root widget.
This is called by [runApp] to configure the widget tree. Consider using
[attachRootWidget] if you want to build the widget tree synchronously.
*/
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
/// 异步执行
Timer.run(() {
attachRootWidget(rootWidget);
});
}
}
void attachRootWidget(Widget rootWidget) {
/// 1、 是不是启动帧, 即看renderViewElement是否有赋值
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
/// 2、 创建RenderObject、Element、Widget关系树, _renderViewElement值为attachToRenderTree方法返回值
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
/// 3、RenderObjectWithChildMixin类型,继承自RenderObject,RenderObject继承自AbstractNode。
/// 4、来自RendererBinding的_pipelineOwner.rootNode,_pipelineOwner来自其初始initInstances方法实例化的PipelineOwner对象。
/// 5、一个Flutter App全局只有一个PipelineOwner实例。
container: renderView,
debugShortDescription: '[root]',
/// 6、外部传进来的Widget, 一般是根导航Widget
child: rootWidget,
/// 7、attach过程,buildOwner来自WidgetsBinding初始化时实例化的BuildOwner实例,renderViewElement值就是_renderViewElement自己,此时由于调用完appach才赋值,所以首次进来也是null。
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
/// 7、首帧主动更新一下,匹配条件的情况下内部本质是调用SchedulerBinding的scheduleFrame()方法。
/// 8、进而本质调用了window.scheduleFrame()方法。
SchedulerBinding.instance!.ensureVisualUpdate();
}
}
}
上面的步骤2, 主要是调用RenderObjectToWidgetAdapter类
## RenderObjectToWidgetAdapter类
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
/// Creates a bridge from a [RenderObject] to an [Element] tree.
///
/// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
/// 1、创建一个桥梁关联RenderObject、Element
RenderObjectToWidgetAdapter({
this.child,
required this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
/// 2、我们编写dart的runApp函数参数中传递的Flutter应用Widget树根, 比如MyApp
final Widget? child;
/// 3、继承自RenderObject,来自PipelineOwner对象的rootNode属性,一个Flutter App全局只有一个PipelineOwner实例。`
final RenderObjectWithChildMixin<T> container;
/// A short description of this widget used by debugging aids.
final String? debugShortDescription;
/// 4、重写Widget的createElement实现,构建了一个RenderObjectToWidgetElement实例,它继承于Element。Element树的根结点是RenderObjectToWidgetElement。
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
/// 5、重写Widget的createRenderObject实现,container本质是一个RenderView。RenderObject树的根结点是RenderView。
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If `element` is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
/*
6、上面代码片段中RenderObjectToWidgetAdapter实例创建后调用
owner来自WidgetsBinding初始化时实例化的BuildOwner实例,element 值就是自己。
该方法创建根Element(RenderObjectToWidgetElement),并将Element与Widget进行关联,即创建 WidgetTree对应的ElementTree。
如果Element已经创建过则将根Element中关联的Widget设为新的(即_newWidget)。
可以看见Element只会创建一次,后面都是直接复用的。
*/
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
/// 7、由于首次实例化RenderObjectToWidgetAdapter调用attachToRenderTree后才不为null,所以当前流程为null
if (element == null) {
/// 8、在lockState里面代码执行过程中禁止调用setState方法
owner.lockState(() {
/*
9、创建一个Element实例,即调用本段代码片段中步骤4的方法
调用RenderObjectToWidgetAdapter的createElement方法构建了一个RenderObjectToWidgetElement实例,继承RootRenderObjectElement,又继续继承RenderObjectElement,接着继承Element。
*/
element = createElement();
assert(element != null);
/// 10、给根Element的owner属性赋值为WidgetsBinding初始化时实例化的BuildOwner实例。
element!.assignOwner(owner);
});
/// 11、重点!mount里面RenderObject, 执行build过程
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
/// 12、更新widget树时_newWidget赋值为新的,然后element数根标记为markNeedsBuild
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
@override
String toStringShort() => debugShortDescription ?? super.toStringShort();
}
上面步骤2接收的外部传进来的widget, 特别重要。因为创建RenderObjectToWidgetElement根节点后, 就会调用RenderObjectToWidgetElement根节点的mount方法, 去生成传进来的widget的element了。具体源码, 我们继续分析。
上面步骤11owner.buildScope(element!, ())
执行build过程, 我们跳进源码看下。
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
......
Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
assert(_debugStateLocked);
Element? debugPreviousBuildTarget;
assert(() {
context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
debugPreviousBuildTarget = _debugCurrentBuildTarget;
_debugCurrentBuildTarget = context;
return true;
}());
_dirtyElementsNeedsResorting = false;
try {
callback();
} finally {
......
}
}
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
/// 针对脏元素执行rebuild操作
_dirtyElements[index].rebuild();
}
} finally {
......
Timeline.finishSync();
}
assert(_debugtateLockLevel >= 0);
}
上面步骤11element!.mount(null, null)
, 我们跳进源码看下。RenderObjectToWidgetElement extends RootRenderObjectElement extends RenderObjectElement extends Element。重点关注的是element的根节点RenderObjectToWidgetElement, 和父类 RenderObjectElement 中的 mount 方法,如下:
RenderObjectToWidgetElement
element根节点RenderObjectToWidgetElement源码如下:
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
/// Creates an element that is hosted by a [RenderObject].
///
/// The [RenderObject] created by this element is not automatically set as a
/// child of the hosting [RenderObject]. To actually attach this element to
/// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree].
RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);
@override
RenderObjectToWidgetAdapter<T> get widget => super.widget as RenderObjectToWidgetAdapter<T>;
/// 1、这个 私有_child特别重要, 它就是外部传进来的widget(MyApp)对象的elment。
Element? _child;
static const Object _rootChildSlot = Object();
@override
void visitChildren(ElementVisitor visitor) {
if (_child != null)
visitor(_child!);
}
@override
void forgetChild(Element child) {
assert(child == _child);
_child = null;
super.forgetChild(child);
}
@override
void mount(Element? parent, Object? newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
assert(_child != null);
}
@override
void update(RenderObjectToWidgetAdapter<T> newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_rebuild();
}
// When we are assigned a new widget, we store it here
// until we are ready to update to it.
Widget? _newWidget;
@override
void performRebuild() {
if (_newWidget != null) {
// _newWidget can be null if, for instance, we were rebuilt
// due to a reassemble.
final Widget newWidget = _newWidget!;
_newWidget = null;
update(newWidget as RenderObjectToWidgetAdapter<T>);
}
super.performRebuild();
assert(_newWidget == null);
}
@pragma('vm:notify-debugger-on-exception')
void _rebuild() {
try {
/// 2、更新操作, 特别重要。 这个_child就是外部传进来的widget(MyApp)对象的elment。
/// 这个方法由基类Elemen提供并实现。
_child = updateChild(_child, widget.child, _rootChildSlot);
} catch (exception, stack) {
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: ErrorDescription('attaching to the render tree'),
);
FlutterError.reportError(details);
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
@override
RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;
@override
void insertRenderObjectChild(RenderObject child, Object? slot) {
assert(slot == _rootChildSlot);
assert(renderObject.debugValidateChild(child));
renderObject.child = child as T;
}
@override
void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
assert(false);
}
@override
void removeRenderObjectChild(RenderObject child, Object? slot) {
assert(renderObject.child == child);
renderObject.child = null;
}
}
RenderObjectElement
RenderObjectElement源码如下:
abstract class RenderObjectElement extends Element {
/// Creates an element that uses the given widget as its configuration.
RenderObjectElement(RenderObjectWidget widget) : super(widget);
/// 1、Element树通过构造方法RenderObjectToWidgetElement持有了Widget树实例。(RenderObjectToWidgetAdapter)。
@override
RenderObjectWidget get widget => super.widget as RenderObjectWidget;
/// 2、Element树通过mount后持有了RenderObject渲染树实例。
@override
RenderObject get renderObject {
assert(_renderObject != null, '$runtimeType unmounted');
return _renderObject!;
}
RenderObject? _renderObject;
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(() {
_debugDoingBuild = true;
return true;
}());
/// 3、通过widget树(即RenderObjectToWidgetAdapter)调用createRenderObject方法传入Element实例自己获取RenderObject渲染树。RenderObjectToWidgetAdapter.createRenderObject(this)返回的是RenderObjectToWidgetAdapter的container成员,也就是上面分析的RenderView渲染树根节点。
_renderObject = widget.createRenderObject(this);
assert(!_renderObject!.debugDisposed!);
assert(() {
_debugDoingBuild = false;
return true;
}());
assert(() {
_debugUpdateRenderObjectOwner();
return true;
}());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
}
Element
因为RenderObjectToWidgetElement根节点调用mount时,执行了Element类中的updateChild方法, 所以, 我们重点关注Element的updateChild、inflateWidget方法.
abstract class Element extends DiagnosticableTree implements BuildContext {
/// Creates an element that uses the given widget as its configuration.
///
/// Typically called by an override of [Widget.createElement].
Element(Widget widget)
: assert(widget != null),
_widget = widget;
Element? _parent;
DebugReassembleConfig? _debugReassembleConfig;
/// The configuration for this element.
@override
Widget get widget => _widget!;
Widget? _widget;
/// Update the given child with the given new configuration.
///
/// This method is the core of the widgets system. It is called each time we
/// are to add, update, or remove a child based on an updated configuration.
///
/// The `newSlot` argument specifies the new value for this element's [slot].
///
/// If the `child` is null, and the `newWidget` is not null, then we have a new
/// child for which we need to create an [Element], configured with `newWidget`.
///
/// If the `newWidget` is null, and the `child` is not null, then we need to
/// remove it because it no longer has a configuration.
///
/// If neither are null, then we need to update the `child`'s configuration to
/// be the new configuration given by `newWidget`. If `newWidget` can be given
/// to the existing child (as determined by [Widget.canUpdate]), then it is so
/// given. Otherwise, the old child needs to be disposed and a new child
/// created for the new configuration.
///
/// If both are null, then we don't have a child and won't have a child, so we
/// do nothing.
///
/// The [updateChild] method returns the new child, if it had to create one,
/// or the child that was passed in, if it just had to update the child, or
/// null, if it removed the child and did not replace it.
///
/// The following table summarizes the above:
///
/// | | **newWidget == null** | **newWidget != null** |
/// | :-----------------: | :--------------------- | :---------------------- |
/// | **child == null** | Returns null. | Returns new [Element]. |
/// | **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
///
/// The `newSlot` argument is used only if `newWidget` is not null. If `child`
/// is null (or if the old child cannot be updated), then the `newSlot` is
/// given to the new [Element] that is created for the child, via
/// [inflateWidget]. If `child` is not null (and the old child _can_ be
/// updated), then the `newSlot` is given to [updateSlotForChild] to update
/// its slot, in case it has moved around since it was last built.
///
/// See the [RenderObjectElement] documentation for more information on slots.
@protected
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
// When the type of a widget is changed between Stateful and Stateless via
// hot reload, the element tree will end up in a partially invalid state.
// That is, if the widget was a StatefulWidget and is now a StatelessWidget,
// then the element tree currently contains a StatefulElement that is incorrectly
// referencing a StatelessWidget (and likewise with StatelessElement).
//
// To avoid crashing due to type errors, we need to gently guide the invalid
// element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
// returns false which prevents us from trying to update the existing element
// incorrectly.
//
// For the case where the widget becomes Stateful, we also need to avoid
// accessing `StatelessElement.widget` as the cast on the getter will
// cause a type error to be thrown. Here we avoid that by short-circuiting
// the `Widget.canUpdate` check once `hasSameSuperclass` is false.
assert(() {
final int oldElementClass = Element._debugConcreteSubtype(child);
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
/// 惊天秘密!!! 首次进入的时候, newChild为空。这里调用了inflateWidget方法, 根据外部传进来的widget(Myapp), 创建widget的elment节点并赋值给_child, 然后返回给根RenderObjectToWidgetElement持有。
newChild = inflateWidget(newWidget, newSlot);
}
....
return newChild;
}
/// Add this element to the tree in the given slot of the given parent.
///
/// The framework calls this function when a newly created element is added to
/// the tree for the first time. Use this method to initialize state that
/// depends on having a parent. State that is independent of the parent can
/// more easily be initialized in the constructor.
///
/// This method transitions the element from the "initial" lifecycle state to
/// the "active" lifecycle state.
///
/// Subclasses that override this method are likely to want to also override
/// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
/// [RenderObjectElement.moveRenderObjectChild], and
/// [RenderObjectElement.removeRenderObjectChild].
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.mount(parent, newSlot)`.
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
assert(_lifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
assert(slot == null);
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance();
}
......
/// Create an element for the given widget and add it as a child of this
/// element in the given slot.
///
/// This method is typically called by [updateChild] but can be called
/// directly by subclasses that need finer-grained control over creating
/// elements.
///
/// If the given widget has a global key and an element already exists that
/// has a widget with that global key, this function will reuse that element
/// (potentially grafting it from another location in the tree or reactivating
/// it from the list of inactive elements) rather than creating a new element.
///
/// The `newSlot` argument specifies the new value for this element's [slot].
///
/// The element returned by this function will already have been mounted and
/// will be in the "active" lifecycle state.
@protected
@pragma('vm:prefer-inline')
Element inflateWidget(Widget newWidget, Object? newSlot) {
assert(newWidget != null);
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!;
}
}
/// 惊天秘密!!! 拿到外部传进来的widget(MyApp), 创建widget(MyApp)的element, 并返回。
final Element newChild = newWidget.createElement();
/// 惊天秘密!!! 生成newChild后, 立即执行elment的mount方法, 挂载节点。
newChild.mount(this, newSlot);
assert(newChild._lifecycleState == _ElementLifecycle.active);
return newChild;
}
}
从上面的源码分析来看, 我们可以得到以下结论:
- Widget树的根节点是RenderObjectToWidgetAdapter(继承自 RenderObjectWidget extends Widget), 我们runApp中传递的Widget树就被追加到了这个树根的child属性上。
- Element树的根节点是RenderObjectToWidgetElement(继承自RootRenderObjectElement extends RenderObjectElement extends Element)。通过调用
RenderObjectToWidgetAdapter
的createElement
方法创建。创建RenderObjectToWidgetElement
的时候把RenderObjectToWidgetAdapter
(创建方法:RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
)通过构造参数传递进去,所以 Element 的 _widget 属性值为 RenderObjectToWidgetAdapter 实例,也就是说 Element 树中 _widget 属性持有了 Widget 树实例。RenderObjectToWidgetAdapter 。 - RenderObject 树的根结点是 RenderView(
RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
),在 Element 进行 mount 时通过调用 Widget 树(RenderObjectToWidgetAdapter)的createRenderObject
方法获取 RenderObjectToWidgetAdapter 构造实例化时传入的 RenderView 渲染树根节点。
上面源码流程对应的时序图如下:
现在我们回到最初的runApp方法的实现中, 探讨下热身帧scheduleWarmUpFrame
scheduleWarmUpFrame热身帧
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
/// 重置时间戳,避免热重载情况从热身帧到热重载帧的时间差,导致隐式动画的跳帧情况。
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
/// 在此次绘制结束前该方法会锁定事件分发,可保证绘制过程中不会再触发新重绘。
/// 也就是说在本次绘制结束前不会响应各种事件。
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
进入布局(Layout)阶段和绘制(Paint)阶段。
这里其实onBeginFrame和onDrawFrame是在具体执行这两个回调。最后渲染出来首帧场景送入engine显示到屏幕。这里使用Timer.run()来异步运行两个回调,是为了在它们被调用之前有机会处理完微任务队列(microtask queue)
总结
WidgetsFlutterBinding是单例模式。
WidgetsBinding的renderViewElement记录着唯一的RenderObjectToWidgetElement对象。 RendererBinding的renderView记录着唯一的RenderView对象。 也就是说每个flutter应用创建的Root Widget跟Element、RenderObject一一对应,且单例唯一。
MyApp是用户定义的根Widget,为了建立三棵树的关系,RenderObjectToWidgetAdapter起到重要的桥接功能,该类的createElement方法创建RenderObjectToWidgetElement对象,createRenderObject()方法获取的是RenderView。
上面就是 Flutter 启动runApp的经历与三棵树的关系, 至于三棵树是如何协同工作的, 我们下一篇再讲。