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