终于放假啦,不用一直写业务代码了,也终于有点时间可以整理整理笔记啦。
我在这里先给大家拜个早年,恭祝大家新春快乐,吉祥安康啦!

Flutter系列学习笔记
- Flutter笔记——handleDrawFrame帧绘制系列之一(源码学习)
- Flutter笔记——runApp发生了什么(源码学习)
- Flutter笔记——State.setState发生了什么(源码学习)
- 用Dart写的身份证号校验代码(可用于flutter项目)
- HelloDart-构造函数特性
- HelloDart-MixIn,土话记录多继承机制
- Flutter笔记——MethodChannel(Native&Flutter数据交互)
- Flutter笔记——FlutterActivity
main
FlutterFramework在Flutter层的项目入口是main函数,默认生成的函数如下
void main() {
runApp(MyApp());
}
1 runApp
runApp是一个顶级函数,接受一个Widget作为rootWidget,这中间发生了什么嘞?
下文以图片、源码和文本解析的方式辅助学习。由于内容较长,需要分为几个部分学习,并且序列图、记录点和实际运行顺序有出入,下文排序主要是为了能够由浅入深的铺开runApp过程中的知识点,帮助更好的理解。
2.1 过程一

-
main函数,调用runApp(Widget)函数///同main函数,runApp函数也是一个顶级函数 void main() { runApp(MyApp()); } - 初始化WidgetsFlutterBinding.instance
void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame(); } -
ensureInitialized函数会创建一个WidgetsFlutterBinding的单例WidgetsBinding.instanceclass WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { ///确认WidgetsBinding.instance是否创建成功,flutter framework工作是是否完成 static WidgetsBinding ensureInitialized() { if (WidgetsBinding.instance == null) WidgetsFlutterBinding(); return WidgetsBinding.instance; } } -
WidgetsFlutterBinding通过mixIn语法继承了7个BindingBase子类,完成整个FlutterFramework层次的系统功能初始化。mixIn语法可参考我的这篇文章HelloDart-MixIn,土话记录多继承机制。abstract class BindingBase { ///会按照WidgetsFlutterBinding集成顺序,以此调用每个WidgetsFlutterBinding子类的 ///initInstances和initServiceExtensions初始化函数。 BindingBase() { developer.Timeline.startSync('Framework initialization'); assert(!_debugInitialized); initInstances(); assert(_debugInitialized); assert(!_debugServiceExtensionsRegistered); initServiceExtensions(); assert(_debugServiceExtensionsRegistered); developer.postEvent('Flutter.FrameworkInitialization', <String, String>{}); developer.Timeline.finishSync(); } } -
initInstances和initServiceExtensions该部分比较复杂,这里先不学习了。简单介绍一下BindingBase系列子类的作用,下列内容我也是看注释的,如有错误烦请指出-
WidgetsFlutterBinding:将FlutterFramework绑定到FlutterEngine上面 -
GestureBinding:绑定手势系统。 -
ServicesBinding:主要作用与defaultBinaryMessenger有关,用于和native通讯相关。 -
SchedulerBinding:改类也继承了ServicesBinding,主要用于调度帧渲染相关事件。 -
PaintingBinding:和painting库绑定 -
SemanticsBinding:将语义层和FlutterEngine绑定起来。 -
RendererBinding:将渲染树与FlutterEngine绑定起来。 -
WidgetsBinding:将Widget层与FlutterEngine绑定起来。
再次重申一下,上述内容只是看注释得来的,如果错漏,感谢指出
-
初始化完WidgetsFlutterBinding.instance及一堆系统Binding之后,便开始下一步操作了。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
这里使用级联语法分别调用了scheduleAttachRootWidget(Widget)和scheduleWarmUpFrame函数,具体过程见下文
2.2 过程二

-
scheduleAttachRootWidget(app):通过级联的方式,生成了WidgetsFlutterBinding.instance静态实例并初始化一干系统功能之后,调用WidgetsFlutterBinding.scheduleAttachRootWidget(Widget root)函数。void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame(); } -
attachRootWidget(Widget):这里异步调用WidgetsBinding.attachRootWidget(Widget rootWidget)函数,只为了尽快显示Flutter项目的UI画面。void scheduleAttachRootWidget(Widget rootWidget) { Timer.run(() { attachRootWidget(rootWidget); }); } -
scheduleWarmUpFrame():在上面第二点中,异步调用attachRootWidget函数,只为了尽快调用该函数,去显示一个帧画面。这个函数的详细流程按下不表,等以后再学习。/// Schedule a frame to run as soon as possible, rather than waiting for /// the engine to request a frame in response to a system "Vsync" signal. void scheduleWarmUpFrame() { ... } -
RenderObjectToWidgetAdapter:接着第2点,attachRootWidget函数作用就是将根Widget、根Element与根RenderObject三个跟对象绑定起来,并将唯一的BuildOwner对象引用作为根对象的持有对象,通过继承关系层层传递。看代码///伪代码 class WidgetsBinding ...{ void attachRootWidget(Widget rootWidget) { //创建一个RenderObjectToWidgetAdapter对象,泛型T是RenderBox类型, _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( container: renderView, debugShortDescription: '[root]',//renderView[注释1]是连接到物理设备输出层的渲染树对象 child: rootWidget, //根Widget树 ).attachToRenderTree(buildOwner, renderViewElement); } } 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]. RenderObjectToWidgetAdapter({ this.child, this.container, this.debugShortDescription, }) : super(key: GlobalObjectKey(container)); /// The widget below this widget in the tree. final Widget child; /// The [RenderObject] that is the parent of the [Element] created by this widget. final RenderObjectWithChildMixin<T> container; @override RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); @override RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container; @override void updateRenderObject(BuildContext context, RenderObject renderObject) { } /// 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; } } -
attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]):这里面关于element的创建先忽略,在步骤3中学习。该部分源码在第四点中,我们可以看到RenderObjectToWidgetAdapter会创建一个RenderObjectToWidgetElement对象,作为WidgetsBinding中的_renderViewElement对象。该_renderViewElement对象也就是整个Element树中的跟对象,其持有根Widget、根RenderView与BuildOwner对象。
2.3 过程三
过程三主要分析RenderObjectToWidgetAdapter.attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ])函数,期间还有一些BaseBinding内容掺杂进来,比较复杂。

-
BuildOnwer.lockState(fn):///该函数中的_debugStateLocked 布尔值,每次调用该函数_debugStateLockLevel都会+1, ///callback运行完之后减1,如果在_debugStateLocked等于true期间调用setState就会抛出异常。 void lockState(void callback()) { assert(callback != null); assert(_debugStateLockLevel >= 0); assert(() { _debugStateLockLevel += 1; return true; }()); try { callback(); } finally { assert(() { _debugStateLockLevel -= 1; return true; }()); } assert(_debugStateLockLevel >= 0); } -
RenderObjectToWidgetAdapter.createElement():会创建一个RenderObjectToWidgetElement对象。
Element依赖关系图.png
这里先不去细看其众多父类中的属性和操作 element.assignOwner(owner):将owner:BuildOnwer赋值给element,该owner:BuildOnwer是整个Element树的统一管理者。-
element.mount(null, null):上图忽略了BuildOwner.buildScope(Element context, [ VoidCallback callback ])函数了,该函数作用在于将一个Element添加进去构建域中,并调用VoidCallback函数作为回调。
这里要着重看下element对象一众父类中的mount函数了owner.buildScope(element, () { element.mount(null, null); }); class RenderObjectToWidgetElement ...{ @override void mount(Element parent, dynamic newSlot) { assert(parent == null); super.mount(parent, newSlot); //这里的child暂时为null,忽略rebuild函数 _rebuild(); } } abstract class RootRenderObjectElement extends RenderObjectElement { @override void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); } } ///RenderObjectElement类中,最重要的属性,_renderObject ///是该根Element的RenderObject对象,该RenderObject类存储 ///了Element的位置信息,Skia是一个2D渲染框架,基于笛卡尔坐标轴 ///RenderObject也实现了基本的渲染功能。 abstract class RenderObjectElement extends Element { /// The underlying [RenderObject] for this element. @override RenderObject get renderObject => _renderObject; RenderObject _renderObject; @override void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); //这个回到步骤二中的RenderObjectToWidgetAdapter类,RendererBinding类初始化之后有个PipelineOwner //对象,PipelineOwner对象中有个rootNode对象,就是该_renderObject了 _renderObject = widget.createRenderObject(this); //这里newSlot等于null,忽略 attachRenderObject(newSlot); //将dirty置为false, _dirty = false; } } class Element ...{ @mustCallSuper void mount(Element parent, dynamic newSlot) { //一顿赋值 _parent = parent; _slot = newSlot; _depth = _parent != null ? _parent.depth + 1 : 1; _active = true; if (parent != null) // Only assign ownership if the parent is non-null _owner = parent.owner; if (widget.key is GlobalKey) { final GlobalKey key = widget.key; key._register(this); } _updateInheritance(); assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }()); } }mount(Element parent, dynamic newSlot)挂载函数在于将Element挂载到Element树上去,如果有parent属性就赋值,挂载之后Element的状态变为active。 -
SchedulerBinding.instance.ensureVisualUpdate():该函数最终调用的WidgetsFlutterBinding.handleDrawFrame()函数,该部分知识在Flutter笔记——runApp发生了什么(源码学习)文章中分析学习。void ensureVisualUpdate() { switch (schedulerPhase) { case SchedulerPhase.idle: case SchedulerPhase.postFrameCallbacks: scheduleFrame(); return; case SchedulerPhase.transientCallbacks: case SchedulerPhase.midFrameMicrotasks: case SchedulerPhase.persistentCallbacks: return; } } -
SchedulerBinding.scheduleFrame():接着便是调用window.scheduleFrame()函数了,这是一个native函数,纯FlutterFramework部分线索就断了。void scheduleFrame() { if (_hasScheduledFrame || !_framesEnabled) return; assert(() { if (debugPrintScheduleFrameStacks) debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.'); return true; }()); ensureFrameCallbacksRegistered(); window.scheduleFrame(); _hasScheduledFrame = true; }
3 小结
这里做个runApp的简单过程图,隐藏了许多细节

- FlutterFramework的Dart部分,程序入口是
main函数。 - 调用
runApp(Widget)函数传入一个Widget作为根Widget。 -
Widget只是一个配置类,不是实际的UI元素。 -
runApp通过WidgetsFlutterBindingmixIn继承一众父类进行初始化。 - 其中,
RendererBinding父类中的renderView对象,是实际的渲染对象。 - 通过
RenderObjectToWidgetAdapter类,生成一个RenderObjectToWidgetElement<RenderBox>类型的Element作为根Element,并让Widget、renderView和BuildOwner和根Element产生关系。 - 挂载根Element,调用
SchedulerBinding.instance.ensureVisualUpdate()函数,等待下一帧渲染。 -
scheduleAttachRootWidget是一个耗时操作,异步运行。runApp会优先调用scheduleWarmUpFrame()渲染预热帧。
上述文章是笔者通过对Flutter1.12版本源码进行学习的总结,如有错漏,还烦请指出纠正,十分感谢。
另外本文只是runApp的源码进行了跟踪学习,Flutter到底是如何渲染到Native设备、还有BaseBinding系列子类作用等知识有待继续学习,而渲染调用的WidgetsFlutterBinding.handleDrawFrame()函数部分知识在Flutter笔记——runApp发生了什么(源码学习)文章中分析学习哈。
