Flutter源码阅读分析:Framework层的启动_董小虫的博客-CSDN博客

baacloud免费翻墙vpn注册使用

来源: Flutter源码阅读分析:Framework层的启动_董小虫的博客-CSDN博客

Framework的启动
本文也发布于本人的知乎专栏:https://zhuanlan.zhihu.com/p/394564792

0. 前言
在我之前的文章Flutter源码阅读分析:引擎初始化与启动的最后,提到了在引擎启动时,会以“main”方法作为主入口函数,执行Dart代码。那么本片文章就从“main”方法着手,分析Dart Framework具体做了什么。

Framework代码:https://github.com/flutter/flutter

1. runApp
首先我们从flutter官方给的例子来看:

// ./examples/hello_world/lib/main.dart
void main() =>
runApp( //[1]
const Center( // [2]
child:
Text(‘Hello, world!’,
key: Key(‘title’),
textDirection: TextDirection.ltr
)
)
);

[1] runApp方法的主要功能是填充给定的Widget并将其附到屏幕上。
[2] 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]
});

}

}

[3] 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的渲染机制,后续在渲染机制中详细分析。

[4] handleBeginFrame方法主要作用是让framework准备好,去创建一个新的帧。它会调用所有通过scheduleFrameCallback注册进来的临时回调函数。
[5] 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方法主要是让渲染管线产生出一帧。

[6] 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();
}

2. 渲染管线
下面看一下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();

}

[7] flushLayout方法用于更新所有“脏”RenderObject的布局信息。该方法时渲染管线中的核心步骤之一。布局信息需要在绘制之前处理“干净”,这样RenderObject就可以在屏幕上出现在最新的位置。
[8] flushCompositingBits方法用于更新RenderObject的needsCompositing字位。
[9] flushPaint方法用于更新所有RenderObject的显示列表,是渲染管线核心步骤之一。该步骤在布局操作之后,场景合成之前。
[10] flushSemantics方法更新RenderObject的语义。该方法也是核心步骤。
4. 总结
Framework的启动从Flutter App的main方法开始,通过runApp方法启动。

将Flutter App中的Widget树附着到WidgetsBinding上;
驱动渲染管线绘制首帧;
再需要的情况下,通过Window的scheduleFrame方法驱动引擎发起新的一帧。
————————————————
版权声明:本文为CSDN博主「董小虫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dongzhong1990/article/details/105798049

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏