flutter⽣成源代码_Flutter源码分析系列(⼀):runApp⽅法究
竟做了什么
⼊⼝
我们从⼀切的起点main.dart说起,这⾥我们⼀定会调⽤runApp⽅法,这个⽅法可以说是Flutter程序的⼊⼝:
void runApp(Widget app) {
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
传⼊的Widget即是我们需要显⽰的界⾯Widget。
继续分析源码,其中WidgetsFlutterBinding是⼀个单例类,WidgetsFlutterBinding继承了BindingBase并且with了⼤量的mixin,可以说这个类就是将Widget架构和Flutter底层Engine连接的桥梁。
ensureInitialized()负责初始化以及返回实例,该⽅法会进⾏⼤量初始化操作。
Widget到Element到RenderObject的流程
attachRootWidget
初始化完成拿到实例后接下来会调⽤attachRootWidget⽅法,该⽅法完成了Widget到Element到RenderObject的整个关联过程,代码如下:
void attachRootWidget(Widget rootWidget) {
_renderViewElement = new RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
实际上就是将传⼊的Widget包装到RenderObjectToWidgetAdapter,它继承⾃RenderObjectWidget,负责将Widget、Element、RenderObject三者关联起来,其中的RenderObject对应前⾯初始化操作中创建的renderView。
其中renderView和_renderViewElement为WidgetsFlutterBinding的成员,可以看出每个app只存在⼀个renderViewElement和renderView,并且⼀⼀对应。
attachToRenderTree
继续看attachToRenderTree⽅法的实现:
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement element]) {
if (element == null) {
owner.lockState(() {
// 创建根Element,RenderObjectToWidgetElement
element = createElement();
element.assignOwner(owner);
});
owner.buildScope(element, () {
// 这⾥会根据WidgetTree构建ElementTree
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
该⽅法负责创建根Element,即RenderObjectToWidgetElement,并且将Element与Widget进⾏关联,即创建出WidgetTree对应的ElementTree。如果Element已经创建过了,则将根Element中关联的Widget设为新的,由此可以看出Element只会创建⼀次,后⾯会进⾏复⽤。
mount
如果Element是⾸次创建,会调⽤mount,该⽅法由⽗类到⼦类会做下⾯⼏件事:
Element
将该Element标记为active的,设置parent为null,slot为null,depth为1,如果对应的widget的key为GlobalKey,在这⾥进⾏注册,即将Key与Element进⾏关联,设置inheritedWidgets,⽤于由上⾄下传递数据。
RenderObjectElement
创建对应的RenderObject,并attach到对应的slot位置。
RootRenderObjectElement
没做什么事,只是assert⼀下parent和slot为null。
RenderObjectToWidgetAdapter
调⽤_rebuild()⽅法创建ElementTree。
如果不是⾸次创建,这种情况⼀般是多次调⽤了runApp⽅法,则更新对应的跟Widget,并调⽤markNeedsBuild()⽅法准备重建ElementTree。
rebuild
下⾯先看下_rebuild()的代码:
void _rebuild() {
try {
// 实际上是调⽤updateChild更新ElementTree
_child = updateChild(_child, widget.child, _rootChildSlot);
} catch (exception, stack) {
// 这⾥就是红屏产⽣的地⽅
final FlutterErrorDetails details = new FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: 'attaching to the render tree'
);
// 这⾥打印了错误栈
/
/ 这⾥就是创建了红屏的Widget,显⽰在屏幕上
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
updateChild
实际上该⽅法只执⾏了updateChild(),该⽅法⾄关重要,ElementTree的⽣成主要就在⽅法中实现,我们来细看⼀下代码,注意代码中添加的注释:
// child表⽰要更新的Element,newWidget表⽰对应Element的Widget,newSlot⽤来标识Element的所在位置,返回该位置对应的新Element
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
assert(() {
// Debug下保证⼀个GlobalKey只对应⼀个Widget
if (newWidget != null && newWidget.key is GlobalKey) {
final GlobalKey key = newWidget.key;
key._debugReserveFor(this);
}
return true;
}());
if (newWidget == null) {
// 如果newWidget为空,child⾮空表⽰需要移除旧Element
if (child != null)
deactivateChild(child);
// 将此Element的位置设为null
return null;
}
if (child != null) {
// 都⾮空且是相同Widget,更新位置标识即可
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
// 更新后返回原Element
return child;
}
// 若不是相同Widget则判断是否有相同的类型和相同的Key,是的话则更新Widget信息到Element
if (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;
}());
/
/ 更新后返回原Element
return child;
}
// 若不符合更新的要求,则抛弃掉原Element,抛弃掉的Element会被回收到`_inactiveElements`列表中,不会⽴即被销毁deactivateChild(child);
assert(child._parent == null);
}
// 其他情况下需要创建新的Element
return inflateWidget(newWidget, newSlot);
}
inflateWidget
继续看inflateWidget⽅法:
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
assert(newWidget != null);
final Key key = newWidget.key;
if (key is GlobalKey) {
// 先使⽤key去被回收的列表中看看是否有可以复⽤的Element
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() { _debugCheckForCycles(newChild); return true; }());
newChild._activateWithParent(this, newSlot);
/
/ 到后就复⽤被回收的Element,并且更新它的Child
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild;
}
}
// 没有可以复⽤的Element了,只能创建新的
final Element newChild = ateElement();
assert(() { _debugCheckForCycles(newChild); return true; }());
// mount新的Element
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
这⾥新创建的Element继续调⽤mount,于是⼜会触发新⼀轮的updateChild,最终对应WidgetTree的整个ElementTree就构建完成了。
开始渲染
回到最初的⼊⼝代码调⽤,最后⼀⾏会调⽤WidgetsFlutterBinding实例的scheduleWarmUpFrame进⾏第⼀次绘制,该⽅法的实现在SchedulerBinding类中。这次draw完成之前都不会接收各种event(触摸事件等等),其他scheduledFrame会被延迟到该次draw完成之后。
该⽅法主要调⽤了handleBeginFrame()和handleDrawFrame()两个⽅法,在看这两个⽅法之前⾸先了解⼀下Frame和FrameCallbacks 的概念:
flutter开发appFrame
Frame即每⼀帧的绘制过程,engine通过VSync信号不断地触发Frame的绘制,实际上就是调⽤SchedulerBinding类中的
_handleBeginFrame()和_handleDrawFrame()这两个⽅法,这个过程中会完成动画、布局、绘制等⼯作。
FrameCallbacks
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论