// Flutter UI绘制区域的大小
Size get physicalSize => _physicalSize;
// 当前系统默认的语言Locale
Locale get locale;
// 当前系统字体缩放比例。
double get textScaleFactor => _textScaleFactor;
// 当绘制区域大小改变回调
VoidCallback get onMetricsChanged => _onMetricsChanged;
// Locale发生变化回调
VoidCallback get onLocaleChanged => _onLocaleChanged;
// 系统字体缩放变化回调
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
// 绘制前回调,一般会受显示器的垂直同步信号VSync驱动,当屏幕刷新时就会被调用
FrameCallback get onBeginFrame => _onBeginFrame;
// 绘制回调
VoidCallback get onDrawFrame => _onDrawFrame;
// 点击或指针事件回调
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
// 调度Frame,该方法执行后,onBeginFrame和onDrawFrame将紧接着会在合适时机被调用,
// 此方法会直接调用Flutter engine的Window_scheduleFrame方法
void scheduleFrame() native 'Window_scheduleFrame';
// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法
void render(Scene scene) native 'Window_render';
// 发送平台消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) ;
// 平台通道消息处理回调
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
... //其它属性及回调
}
Window类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。通过这些Binding 监听Window对象的一些事件,然后将这些事件按照Framework的模型包装,抽象再分发。
2、scheduleAttachRootWidget
WidgetsFlutterBinding初始化之后,接着会调用WidgetsBinding.attachRootWidget方法,该方法负责将根Widget添加到RenderView上,
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement?);
}
注意:
代码中的renderView是一个RenderObject,它渲染树的根
renderViewElement是renderView对应的Element对象,可见该方法主要完成根widget到根RenderObject再到跟Element的整个关联过程。
再来看看attachToRenderTree源码实现:
/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If element is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement? 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,即:RenderObjectToWidgetElement,并且将element于widget进行关联,即创建出widget数对对应的element树。如果element已经创建过了,则将根element中关联的widget设为新的,由此可以看出element只会创建一次,后面会进行复用。那么BuildOwner是什么呢?其实它就是widget fragment的管理类,它跟踪哪些widget需要重新构建。
3、热身帧绘制
组件数在构建(build)完成以后,回到runApp实现中,当attachRootWidget后,最后一行调用WidgetsFlutterBinding实例的scheduleWarmUpFrame()方法,该方法在实例SchedulerBinding中,它被调用后会立即进行一次绘制,在此次绘制结束之前,该方法会锁定事件分发,也就是说在本次绘制结束完成之前Flutter将不会响应各个事件,这可以保证在绘制过程中不会被再出发新的绘制。
scheduleWarmUpFrame()源码
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
这个函数其实就调用了两个函数,onBeginFrame和onDrawFrame,最后渲染出来的首帧场景送入engine显示到屏幕。这里使用 Timer.run()来异步运行两个回调,就是为了在他们被调用之前有机会处理完微任务队列(microtaskqueue)。
我们之前说渲染流水线是由Vsync信号驱动的,但是上述过程都是在runApp()里完成的。并没有看到什么地方告诉engine去调度一帧。这是因为我们是在做Flutter的初始化。为了节省等待Vsync信号的时间,所以就直接把渲染流程跑完做出来第一帧图像来了。