在我之前的文章Flutter源码阅读分析:引擎初始化与启动的最后,提到了在引擎启动时,会以“main”方法作为主入口函数,执行Dart代码。那么本片文章就从“main”方法着手,分析Dart Framework具体做了什么。
Framework代码:https://github.com/flutter/flutter
首先我们从flutter官方给的例子来看:
// ./examples/hello_world/lib/main.dart
void main() =>
runApp( //[1]
const Center( // [2]
child:
Text('Hello, world!',
key: Key('title'),
textDirection: TextDirection.ltr
)
)
);
runApp
方法的主要功能是填充给定的Widget
并将其附到屏幕上。Center
是一种将子节点置于中心的Align
,这些都是Fllutter的Widget系统,这个在后文中详细分析。此处先看runApp
做了什么:
// ./packages/flutter/lib/src/widgets/binging.dart
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
如果重复调用runApp
方法,那么会将之前的根Widget
从屏幕中移除,并将新的指定的Widget
替换到该位置。这个新的Widget树与前者进行对比,并将区别的地方应用到后续的渲染树。这里涉及到Flutter的布局渲染机制,会在后续讲解。
WidgetsFlutterBinding
类是基于Widget框架的应用程序的具体绑定。这将框架和Flutter引擎绑定起来。
// ./packages/flutter/lib/src/widgets/binding.dart
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
ensureInitialized
方法返回一个WidgetBinding
的实例。
再看一下scheduleAttachRootWidget
和scheduleWarmUpFrame
方法:
// ./packages/flutter/lib/src/widgets/binding.dart
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
...
// 通过Timer安排一个任务,用于执行附着根Widget
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget); // [3]
});
}
...
}
// ./packages/flutter/lib/src/scheduler/binding.dart
mixin SchedulerBinding on BindingBase {
...
// 以最快速度安排一帧,而不是等待引擎为响应系统“Vsync”信号而请求一帧。
// 这个方法用于在应用启动时,这样第一帧可以得到多一些时间来运行。
void scheduleWarmUpFrame() {
...
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
handleBeginFrame(null); // [4]
});
Timer.run(() {
handleDrawFrame(); // [5]
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame(); // [6]
});
...
}
...
}
attachRootWidget
方法将一个Widget
附着到renderViewElement
。// ./packages/flutter/lib/src/widgets/binding.dart
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
这里涉及到的Widget
、Element
、Render
,都属于Flutter的渲染机制,后续在渲染机制中详细分析。
handleBeginFrame
方法主要作用是让framework准备好,去创建一个新的帧。它会调用所有通过scheduleFrameCallback
注册进来的临时回调函数。handleDrawFrame
方法主要作用是创建一个新的帧。这个方法一般紧跟着handleBeginFrame
方法后调用。// ./packages/flutter/lib/src/scheduler/binding.dart
void handleDrawFrame() {
...
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
...
}
handleDrawFrame
会调用所有通过addPersistentFrameCallback
方法注册进来的回调函数(这些回调函数通常都是驱动渲染管线),以及通过addPostFrameCallback
方法注册进来的回调函数。
在RendererBinding
类的initInstantces
方法中调用了addPersistentFrameCallback
方法。其添加的回调函数如下:
// ./packages/flutter/lib/src/rendering/binding.dart
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_mouseTracker.schedulePostFrameCheck();
}
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
drawFrame
方法主要是让渲染管线产生出一帧。
scheduleFrame
则是在需要的时候,通过调用Window
的scheduleFrame
方法安排一个新的帧。// ./packages/flutter/lib/src/scheduler/binding.dart
void scheduleFrame() {
...
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame = true;
}
这里的Window
类是在引擎内实现的,通过Dart引擎扩展API,调用到C++的ScheduleFrame
方法:
// (engine)./lib/ui/window/window.cc
void ScheduleFrame(Dart_NativeArguments args) {
UIDartState::Current()->window()->client()->ScheduleFrame();
}
下面看一下PipelineOwner
,这个类是渲染树的持有者,维护布局、合成、绘制和可达性的状态。
PipelineOwner
主要方法有以下几个
// ./packages/flutter/lib/src/rendering/object.dart
void flushLayout() { //[7]
...
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = <RenderObject>[];
for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize();
}
}
...
}
void flushCompositingBits() { // [8]
...
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
...
}
void flushPaint() { // [9]
...
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first).
for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
...
}
void flushSemantics() { // [10]
...
final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
_nodesNeedingSemantics.clear();
for (final RenderObject node in nodesToProcess) {
if (node._needsSemanticsUpdate && node.owner == this)
node._updateSemantics();
}
_semanticsOwner.sendSemanticsUpdate();
...
}
flushLayout
方法用于更新所有“脏”RenderObject
的布局信息。该方法时渲染管线中的核心步骤之一。布局信息需要在绘制之前处理“干净”,这样RenderObject
就可以在屏幕上出现在最新的位置。flushCompositingBits
方法用于更新RenderObject
的needsCompositing
字位。flushPaint
方法用于更新所有RenderObject
的显示列表,是渲染管线核心步骤之一。该步骤在布局操作之后,场景合成之前。flushSemantics
方法更新RenderObject
的语义。该方法也是核心步骤。Framework的启动从Flutter App的main
方法开始,通过runApp
方法启动。
Widget
树附着到WidgetsBinding
上;Window
的scheduleFrame
方法驱动引擎发起新的一帧。