前言
从上一篇文章 Widget,Element,RenderObject树的构建和更新流程 分析了Flutter中的节点的构建,从源码出发,说明节点是如何一步步地往下构建,在文章也提到了WidgetsFlutterBinding,但是没有细说。
在这篇文章里面,主要分析App的构建过程,分析从runApp以后发生了什么事情。分析一下WidgetsFlutterBinding是什么东西
文章后方也有一个流程图,可以通过看流程图了解app的启动流程
runApp做了什么?
我们的main函数里面,通常都会调用runApp(Widget app)方法,那这个方法里面到底做了什么呢?runApp的实现如下
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()//初始化
..scheduleAttachRootWidget(app)//绑定根widget
..scheduleWarmUpFrame();//确保尽快第一帧
}
这个方法里面调用了WidgetsFlutterBinding的三个方法,分别用于初始化WidgetsFlutterBinding,和绑定widget,并开始构建页面。先分析一下这个WidgetsFlutterBinding是什么东西。
Flutter中的Binding
Flutter中的Binding概念我理解为一个胶水层,负责连接framework和engine层,关系大致如下
runApp方法就是创建一个胶水层WidgetsFlutterBinding,并连接framework和engine层。
WidgetsFlutterBinding
WidgetsFlutterBinding是一个Binding的实例,也就是一条桥,负责连接Flutter的engine和framework的之间的事件交互。WidgetsFlutterBinding的定义如下
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
WidgetsFlutterBinding继承了很多BindingBase。然后with了很多mixin类,而且这些mixin都是继承于BindingBase这个类
BindingBase这个类的方法比较多,抽出来与本文相关的一些方法,如下
ui.SingletonFlutterWindow get window => ui.window;
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
void initInstances();
void initServiceExtensions();
其中的这个window是SingletonFlutterWindow类型的,位于Flutter 的engine层。SingletonFlutterWindow就是我们Flutter app加载的窗口,而且SingletonFlutterWindow是一个单例,它继承于FlutterView。
FlutterView中有一个方法
PlatformDispatcher get platformDispatcher;
这个方法返回一个platformDispatcher,这个platformDispatcher是Flutter 的一个事件分发器,负责Flutter分发engine的事件,和传递事件给engine层。
SingletonFlutterWindow继承自FlutterView。也就继承了platformDispatcher。也就是说SingletonFlutterWindow是通过platformDispatcher去处理来自Flutter engine层的事件, platformDispatcher将flutter engine的事件分发到SingletonFlutterWindow,把SingletonFlutterWindow的事件传给flutter engine。
注意一下,BindingBase里面也有一个platformDispatcher,BindingBase和FlutterView中的platformDispatcher是同一个。
WidgetsFlutterBinding.ensureInitialized()
现在说回WidgetsFlutterBinding,当在runApp中调用了WidgetsFlutterBinding.ensureInitialized的时候,会返回一个instance.如果instance为空,则调用默认的构造函数去创建一个instance。
默认构造函数在BindingBase的实现如下
BindingBase() {
...
initInstances();//实例化
...
initServiceExtensions();//注册服务
...
}
因为WidgetsFlutterBinding最后with的是WidgetsBinding了,WidgetsBinding是继承了BindingBase,ServicesBinding,SchedulerBinding,PaintingBinding,GestureBinding,RendererBinding,SemanticsBinding。所以这些mixin的initInstances和initServiceExtensions方法也会一并执行。WidgetsFlutterBinding和其with的mixin都会一同实例化并注册服务。
其中各个mixin负责的部分如下
- BindingBase 各个binding相关的mixin的基类
- ServicesBinding 处理与原生的交互通道
- GestureBinding 处理手势
- RendererBinding 处理渲染
- PaintingBinding 处理绘制相关
- SemanticsBinding 处理语义化
- WidgetsBinding 处理widget
其中需要注意的是
- WidgetsBinding中的initInstances会初始化一个BuildOwner,处于管理widget和Element树
- RendererBinding中的initInstances会初始化一个PipelineOwner,用于管理RenderObjct树,也会去创建一个callback,这个callback在每一帧刷新的时候都会调用
binding层和SingletonFlutterWindow,WidgetsFlutterBinding做交互。其中WidgetsFlutterBinding中的mixin都负责某一块的功能。
大致关系如下
WidgetsFlutterBinding.scheduleAttachRootWidget
WidgetsFlutterBinding实例化后会继续调用scheduleAttachRootWidget方法。实现如下
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
}
在上一篇文章我们说过attachRootWidget方法用于是为根widget生成一个根Element.生成Element调用了attachToRenderTree方法并传入了BuildOwner和Element. attachRootWidget的实现如下
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!;
}
这里的处理逻辑我在上一篇文章也讲到过,具体关于节点树的构建可以参看上一篇文章。当element为空时,会调用SchedulerBinding.instance!.ensureVisualUpdate()的方法。这一个方法也很重要,经过下面的过程
ensureVisualUpdate() -> scheduleFrame() -> ensureFrameCallbacksRegistered()
最终会调用ensureFrameCallbacksRegistered方法,ensureFrameCallbacksRegistered方法如下
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
经过一些列的调用,最终会把SchedulerBinding中的_handleBeginFrame和_handleDrawFrame这两个callback传给WidgetsFlutterBinding类的window
set onBeginFrame(FrameCallback? callback) {
platformDispatcher.onBeginFrame = callback;
}
set onDrawFrame(VoidCallback? callback) {
platformDispatcher.onDrawFrame = callback;
可以看到,window中的方法会把callback传给platformDispatcher。 也就是说WidgetsFlutterBinding.scheduleAttachRootWidget这个方法经过一系列的方法调用后,最终会把SchedulerBinding这个mixin的_handleBeginFrame和_handleDrawFrame传给platformDispatcher。
前面说到,platformDispatcher分发来自enginee的事件。而在这里SingletonFlutterWindow把platformDispatcher的onBeginFrame和onDrawFrame这两个事件交给SchedulerBinding处理。
当硬件发出VSync信号时,会调用platformDispatcher的onDrawFrame。从上面可知,实际上会调用SchedulerBinding中的_handleDrawFrame方法。_handleDrawFrame会调用handleDrawFrame方法。handleDrawFrame方法定义如下
void handleDrawFrame() {
...
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
...
其中的_postFrameCallbacks里面存储的是callback,作用是硬件每次发出VSync信号的时候都会调用。
这个方法里面会取出_postFrameCallbacks里面所有的callback,然后执行callback。这里的_postFrameCallbacks是在RenderBinding这个mixin的initInstances方法中传入的,如下
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
...
addPersistentFrameCallback(_handlePersistentFrameCallback);
...
}
在RenderBinding的initInstances方法做了两件事
- 初始化了一个PipelineOwner用于管理RenderObject.
- 将_handlePersistentFrameCallback这个callback传入SchedulerBinding中的_postFrameCallbacks中,这样在硬件每次发出VSync信号的时候都会调用RenderBinding中的_handlePersistentFrameCallback方法._handlePersistentFrameCallback方法中直接调用了drawFrame方法。
因为在WidgetsBinding中实现了drawFrame方法,所以会调用WidgetsBinding中的drawFrame方法。 如下
void drawFrame() {
...
if (renderViewElement != null)
buildOwner!.buildScope(renderViewElement!);//构建更新子树
super.drawFrame();
buildOwner!.finalizeTree();告诉buildOwner可以完成更新了,可以释放掉_inactiveElements
...
这个方法会先调用buildOwnerOwner的buildScope方法去构建和更新节点树。然后调用super.drawFrame()方法去调用RederBinding的drawFrame方法。如下
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();//刷新布局
pipelineOwner.flushCompositingBits();//图层更新
pipelineOwner.flushPaint();//刷新绘制
if (sendFramesToEngine) {
renderView.compositeFrame(); // 告诉GPU渲染renderView
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
这个方法会去调用去管理RenderObject树生成新的UI绘制信息,交给renderView,并生成一个Scene给SingletonFlutterWindow,然后GPU去渲染新的界面
硬件每次发出VSync信号处理流程如下
关于图片中的一些补充
- BuildOwner是在WidgetsnBiding的initInstances方法中生成的
- PielineOwner是在RenderBinding的initInstances方法中生成的
- RenderView是在RenderBinding的initInstances方法中生成的
WidgetsFlutterBinding.ensureInitialized()
这个方法最重要就是调用window.scheduleFrame()方法,从而调用platformDispatcher.scheduleFrame()方法,去通知engine层需要绘制。engine会根据情况尽快地调用platformDispatcher的onDrawFrame方法。
顺便一提的是,当我们开发过程中调用了setState()方法的时候,会调用BuildOwner的scheduleBuildFor方法,scheduleBuildFor方法中会调用onBuildScheduled这个callback,onBuildScheduled是由WigetsBinding这个mixin中传入的。对应着WigetsBinding的_handleBuildScheduled这个方法。
_handleBuildScheduled这个方法最终也是会调用window.scheduleFrame()通知engine更新界面。
总结
runApp这个方法总的来说就是做了以下的事情
- 在Flutter的framework层和engine层建立一个连接WidgetsFlutterBinding,注册Vsync回调后,每一帧调用的时候都会触发WidgetsFlutterBinding里面的方法,从而去调用framework层的处理逻辑
- 为传入的widget构建节点树,将节点树中的RenderObjct树的结果交给enginee层的SingletonFlutterWindow,然后通知到GPU进行渲染