Flutter渲染原理系列之runApp执行流程
前言
一、runApp
---->[1/5main.dart]----
void main() {
runApp( const MyApp());
}
---->[2/5 src/widgets/binding.dart]----
void runApp(Widget app){
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
1、Flutter
应用的执行始于main.dart
文件中的main
函数,这是Dart
程序的入口点。
2、runApp
方法,位于src/widgets/binding.dart
中的全局方法
,其参数为一个Widget
对象,该Widget
将成为应用的根组件
。
二、WidgetsFlutterBinding
---->[3/5 src/widgets/binding.dart]----
/// A concrete binding for applications based on the Widgets framework.
/// 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;
}
}
1、是Flutter
框架中的一个核心类,它扮演着桥梁
的角色,连接Dart
层的Widget框架
和底层的Flutter引擎
。
2、继承自BindingBase
,并且混入了七大xxxBinding
,只有一个静态方法ensureInitialized
,该方法返回WidgetsBinding
单例对象。
三、BindingBase
abstract class BindingBase {
BindingBase() {
//...
initInstances();
//...
}
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
此抽象类中需要重点关注两点:
1、在构造函数中会调用mixin
的七大xxxBinding
绑定类各自initInstances
方法。
2、platformDispatcher
(平台分发器),是Flutter
中的一个核心接口,用于处理与平台(如iOS
或Android
)交互的事件。它是Flutter引擎
和框架
之间的桥梁
,使得框架能够访问和响应来自引擎的事件,如输入事件(触摸、鼠标、键盘等)、画面帧绘制请求等。
四、WidgetsBinding
---->[4/5 src/widgets/binding.dart]----
///The glue between the widgets layer and the Flutter engine.
mixin WidgetsBinding on BindingBase, ServicesBinding,SchedulerBinding,
GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
//...
}
/// Schedules a [Timer] for attaching the root widget.
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
/// Takes a widget and attaches it to the [renderViewElement], creating it if necessary.
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
}
1、Widgets
和Flutter引擎
之间的粘合剂,用来连接Widget
和engine
。
2、该类创建了BuildOwner
对象,它是构建期间
(build
)的管理者,主要负责管理Widget
的重建,并且设置了onBuildScheduled
回调。
3、在scheduleAttachRootWidget
方法中开启一个任务,主要用于将应用程序的根Widget
附加到渲染树中,渲染前的准备。
4、attachRootWidget
方法的主要作用是将根rootWidget
绑定到渲染树rendeViewElement
中。这里的renderView
是在初始化RenderBinding
时创建的RenderView
实例,也是渲染树的根节点
。
五、RenderObjectToWidgetAdapter
---->[5/5src/widgets/binding.dart]----
/// A bridge from a [RenderObject] to an [Element] tree.
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
RenderObjectToWidgetAdapter({
this.child,
required this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
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);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
}
1、此组件是一个特殊的RenderObjectWidget
,它在Widget层
和渲染层
之间充当桥梁
,将Widget
树上的节点
映射到渲染树上的RenderObject节点
。
2、createElement
方法会实例化该类型元素,并将组件自身作为入参,返回一个RenderObjectToWidgetElement
对象。
3、attachToRenderTree
方法的主要作用是将Widget
树中的根Widget
与渲染树中的根RenderObject
进行关联。
attachToRenderTree
方法的主要功能:
- 构建
Element
树:- 该方法首先会创建一个新的
RenderObjectToWidgetElement
,用于将RenderObject
与Widget
连接起来。它会从当前的RenderObjectToWidgetAdapter
实例中获取rootWidget
,并使用此rootWidget
创建Element
树的根Element
。
- 该方法首先会创建一个新的
- 关联
RenderObject
:- 同时,还会关联
RenderObject
树的根节点
。这意味着它会设置RenderObject
树中的根节点
与Element
树中的根Element
相对应。
- 同时,还会关联
- 构建和更新
Element
树:- 它会调用
BuildOwner
的lockState
方法来保证在构建过程中没有状态更改,然后创建Element
树。如果之前已经有Element
树存在,那么会更新现有的Element
树,以反映任何在Widget
树中的变化。
- 它会调用
- 触发
布局
和绘制
:- 在
Element
树建立或更新完毕后,它会触发渲染树的布局和绘制过程,以确保屏幕上的内容与新的Widget
树匹配。
- 在
attachToRenderTree
方法执行完成后,会根据Widget
生成对应的Element
树和RenderObject
树。此阶段对应于渲染流水线的build
阶段。
六、scheduleWarmUpFrame
void scheduleWarmUpFrame() {
Timer.run(() {
handleBeginFrame(null);
});
Timer.run(() {
handleDrawFrame();
});
}
1、安排一个帧尽快运行,而不是等待引擎根据系统Vsync
信号请求帧。
2、此方法中开启了两个任务,从此就进入了渲染流水线中的layout
阶段和paint
阶段。
3、至此,runApp
里调用的最后一个函数的执行完成。
七、总结
runApp
执行流程的主要步骤如下:
1、初始化
:
- 当
runApp
被调用时,首先会检查WidgetsFlutterBinding
实例是否存在,若无则会创建该实例。
2、设置root widget
:
runApp
接受一个Widget
作为参数,该Widget
将成为应用程序的根Widget
。
3、构建Element
树:
runApp
会创建一个Element
树的根节点
,这通常是一个ComponentElement
。这个过程涉及构建一个与Widget
树对应的Element
树,其中每个Widget
都有一个对应的Element
。
4、创建RenderObject
树:
- 接下来,
runApp
会构建一个RenderObject
树,这是实际负责布局
和绘制
的树。
5、初始化Binding
:
runApp
会调用WidgetsFlutterBinding.instance.attachRootWidget
方法,这将触发一系列初始化操作,包括设置SchedulerBinding
和PaintingBinding
,以及初始化SemanticsBinding
和GesturesBinding
。
6、触发构建
和布局
:
runApp
会请求一个新的帧,这会触发Element
树的构建过程,接着是RenderObject
树的布局和绘制。这通常涉及调用build
方法来创建Widget
的子树,然后调用layout
和paint
方法来安排和绘制RenderObject
。
7、事件循环
:
- 一旦应用程序的初始
布局
和绘制
完成,事件循环将开始监听和处理各种事件,如用户输入和定时器回调。runApp
之后的执行将由这些事件驱动
。
8、生命周期管理
:
runApp
还涉及到生命周期管理,例如,在StatefulWidget
的情况下,initState
方法会在Widget
第一次构建时被调用。
总而言之,runApp
不仅启动了应用程序的初始化,还建立了Widget
、Element
和RenderObject
之间的联系,从而使得应用程序的UI
能够在屏幕上正确地呈现和响应用户交互。
码字不易,记得关注 + 点赞 + 收藏
转载自:https://juejin.cn/post/7399984522094428187