博主相关文章列表
Flutter 框架实现原理
Flutter 框架层启动源码剖析
Flutter 页面更新流程剖析
Flutter 事件处理源码剖析
Flutter 路由源码剖析
Flutter 安卓平台源码剖析(一)
Flutter 自定义控件之RenderObject
PS: 剖析源码版本为Flutter 1.18
入口函数,其主要作用是注入给定的小控件并将其附加到屏幕上。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
WidgetsBinding
的全局单例widget
并添加到RenderView
上,在这个过程中完成Element
树和RenderObject
树的生成static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
这里通过子类WidgetsFlutterBinding
示例化了一个WidgetsBinding
对象。但是这里WidgetsFlutterBinding
没有显式声明构造方法,因此我们查看它的父类的构造方法实现
// [..\flutter\packages\flutter\lib\src\foundation\binding.dart]
BindingBase() {
// 删除断言
developer.Timeline.startSync('Framework initialization');
initInstances();
initServiceExtensions();
developer.postEvent('Flutter.FrameworkInitialization', {});
developer.Timeline.finishSync();
}
可以看到,这里面主要调用了initInstances()
做了一些初始化操作,但是基类BindingBase
自己的initInstances()
是一个空的实现,因此要查看其他父类中的实现。这里我们需要注意到WidgetsFlutterBinding
类的继承情况
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
// ......
}
由此,这里initInstances()
方法会调用最外侧的WidgetsBinding
中的具体实现。检查代码可知,混入的每一个类中都实现了initInstances
方法,并且还调用了super.initInstances()
,这样一来,就会从最外层的WidgetsBinding
开始,依次链式调用每一个混入类中的initInstances()
方法,完成各个Binding
的初始化。
接下来我们先进入WidgetsBinding
中的initInstances
实现,逻辑不多,主要是创建了BuildOwner
对象,并给window
设置了一些回调函数
/// [WidgetsBinding]
void initInstances() {
super.initInstances();
_instance = this;
_buildOwner = BuildOwner();
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
再查看RendererBinding
中的initInstances
实现,这里创建了PipelineOwner
对象,也给window
设置了另一些回调函数
/// [RendererBinding]
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
// 创建一个根RenderObject
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
// 注册persistentCallbacks回调
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
}
这里我们可以查看一下根RenderObject
的创建
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
这里其他Binding
的初始化先略过,我们查看一下出现多次的window
是什么东西。根据官方的解释,Window
是Flutter 框架连接宿主操作系统的接口
class Window {
// 当前设备的DPI,即一个逻辑像素显示多少物理像素,数字越大,显示效果就越精细保真。
// DPI是设备屏幕的固件属性,如Nexus 6的屏幕DPI为3.5
double get devicePixelRatio => _devicePixelRatio;
// 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;
... //其它属性及回调
}
我们可以发现,混入的那些Binding
基本都是监听并处理Window
对象的一些事件,然后将这些事件根据Flutter框架层的模型进行包装、抽象最后分发。
我们查看官方 WidgetsFlutterBinding文档可知,WidgetsFlutterBinding
是将Framework与Flutter引擎绑定的胶水。BindingBase
相当于所有Binding
的基类,定义了一些共同行为。
GestureBinding
: 绑定Framework手势子系统,是Framework事件模型与底层事件的绑定入口ServicesBinding
: 用于绑定平台消息通道(message channel),主要处理原生和Flutter通信SchedulerBinding
:监听刷新事件,绑定Framework绘制调度子系统PaintingBinding
: 绑定绘制库,主要用于处理图片缓存。SemanticsBinding
:语义化层与Flutter 引擎的桥梁,主要是辅助功能的底层支持RendererBinding
: 是渲染树与Flutter 引擎的桥梁WidgetsBinding
: 它是Flutter Widget层与引擎的桥梁详细描述参见官方文档:
再看scheduleAttachRootWidget
方法,它实际上调用了如下方法,它完成了 Widget
、RenderObject
和Element
三者的关联。具体代码在attachToRenderTree
方法中
/// [WidgetsBinding]
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement);
}
BuildOwner
是widget
框架的管理器类。该类跟踪哪些widgets需要重建,并处理其他适用于widgets树的任务,如管理树的非活动元素列表,并在调试时的热重载期间在必要时触发 "reassemble "命令。
/// [RenderObjectToWidgetAdapter]
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
/// 首次进来,element为空,因此创建根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
为null
,所以执行createElement
方法创建Element
,而真正执行构建树的操作是 owner.buildScope
方法。这个方法首先执行传入的回调,即执行element.mount(null, null)
方法
/// [RenderObjectToWidgetElement]
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
mount
方法会首先调用父类的mount
方法,即调用到RenderObjectElement
类中的mount
方法如下,在此处创建RenderObject
对象,同时调用attachRenderObject
方法生成RenderObject
树
/// [RenderObjectElement]
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
/// 代码已删除断言
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
再看_rebuild
方法,其中主要调用了updateChild
方法,updateChild
方法的主要作用是用给定的新配置(widget)更新给定的子元素。这里可以关注catch
中的代码,正是此处生成了Flutter中常见的红屏报错页面ErrorWidget
。
/// [RenderObjectToWidgetElement]
void _rebuild() {
try {
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
} catch (exception, stack) {
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: ErrorDescription('attaching to the render tree'),
);
FlutterError.reportError(details);
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
updateChild
方法移除注释与断言后如下,newWidget
和child
的值存在几种不同的组合情况,详见相关类分析篇。当首次进入时,会执行inflateWidget
,为根Widget的子Widget创建一个新的Element
。
/// [Element]
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
newChild = child;
} else {
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
inflateWidget
方法删除断言后如下,可以看到,这里创建newChild
后又调用了mount
方法,依次递归遍历Widget树的子节点,不断为Widget
创建对应的Element
、RenderObject
,以此完成“三颗树”的构建与关联。
/// [Element]
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild;
}
}
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
接下来调用scheduleWarmUpFrame
进行了第一次绘制,具体实现在SchedulerBinding
中。该方法会锁定事件调度直到完成为止。即在这次绘制完成之前都不会接收event(触摸事件等)。
该方法中主要调用了handleBeginFrame()
和handleDrawFrame()
/// [SchedulerBinding]
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// 在这里使用计时器来确保microtasks在两者之间刷新
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame();
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// 锁定事件,使触摸事件等在预定帧结束前不会自行插入。
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
其中handleBeginFrame
方法用于渲染前的一些准备,主要是处理transientCallbacks
回调,也就是触发动画相关的 Ticker 回调。而真正处理渲染的是handleDrawFrame
方法,它由引擎调用以生成新帧。
/// [SchedulerBinding]
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// 处理persistentCallbacks回调
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// 处理postFrameCallbacks回调
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List localPostFrameCallbacks =
List.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
_currentFrameTimeStamp = null;
}
}
根据官方文档的解释,有以下三个回调队列(参见SchedulerBinding文档)
transientCallbacks
:由系统的Window.onBeginFrame 回调触发,一般用于处理动画的回调。persistentCallbacks
:由系统的Window.onDrawFrame 回调触发。永久callback,一经注册无法移除,由WidgetsBinding.instance.addPersitentFrameCallback()
注册,主要处理布局与绘制工作。postFrameCallbacks
:在persistentCallbacks
回调之后,Window.onDrawFrame 返回之前执行。它只会调用一次,调用后就会被系统移除。可由WidgetsBinding.instance.addPostFrameCallback()
注册,通常用于State的更新。可以看到,这里的代码逻辑主要就是处理persistentCallbacks
和postFrameCallbacks
的回调,而框架正好又在RendererBinding
初始化时注册了一个persistentCallbacks
回调
/// [..\flutter\packages\flutter\lib\src\rendering\binding.dart]
/// [RendererBinding]
void initInstances() {
super.initInstances();
_instance = this;
/// 省略部分代码
addPersistentFrameCallback(_handlePersistentFrameCallback);
/// .......
}
void _handlePersistentFrameCallback(Duration timeStamp) {
// 注意,这里不是调用本类中的方法,而是依据WidgetsFlutterBinding混入的顺序调用
// 实际上是调用的WidgetsBinding的drawFrame方法
drawFrame();
_mouseTracker.schedulePostFrameCheck();
}
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // 这会将二进制数据发送到GPU
pipelineOwner.flushSemantics(); // 将语义发送给操作系统
_firstFrameSent = true;
}
}
以上代码中需要特别注意,回调中并不是直接调用RendererBinding
中的drawFrame()
方法,而是依据WidgetsFlutterBinding
混入的顺序调用
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {}
根据Dart mixin
语法,混入多个类后,调用同名方法时,是从最外层开始调用。可以看到,这里是调用的WidgetsBinding.drawFrame()
/// [WidgetsBinding]
/// 抽取构建和渲染管道以生成一帧。
/// 这个方法被handleDrawFrame调用,当需要布局和绘制一帧时,引擎会自动调用这个方法。
void drawFrame() {
TimingsCallback firstFrameCallback;
if (_needToReportFirstFrame) {
firstFrameCallback = (List timings) {
if (!kReleaseMode) {
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', {});
}
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
firstFrameCallback = null;
_firstFrameCompleter.complete();
};
// 只有在调用[Window.render]时才会调用回调。
// 当[sendFramesToEngine]在帧中被设置为false时,它将不会被调用,我们需要删除回调
SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
}
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
}
if (!kReleaseMode) {
if (_needToReportFirstFrame && sendFramesToEngine) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
if (firstFrameCallback != null && !sendFramesToEngine) {
// 这个帧是延时的,并不是第一个发送给引擎的应该报告的帧
_needToReportFirstFrame = true;
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
}
}
实际上,这里的主要逻辑是try
中的代码
try {
if (renderViewElement != null)
// 将被标记为dirty的Element进行rebuild()
buildOwner.buildScope(renderViewElement);
// 调用父类的drawFrame,这里实际上调用的是RendererBinding中的drawFrame()方法
super.drawFrame();
// 通过卸载任何不再活动的元素来完成元素构建过程
buildOwner.finalizeTree();
}
其中通过调用super.drawFrame()
又再次回到RendererBinding
中的drawFrame()
方法
// 更新需要计算布局的渲染对象。在这个阶段,计算每个渲染对象的大小和位置。
pipelineOwner.flushLayout();
// 更新具有dirty compositing bits的所有渲染对象。 在此阶段,每个渲染对象将了解其子对象是否需要合成。
// 在选择如何实现视觉效果(例如剪裁)时,在绘画阶段将使用此信息。
pipelineOwner.flushCompositingBits();
// 访问需要绘制的渲染对象进行绘制
pipelineOwner.flushPaint();
// 该方法将画好的layer传给引擎,该方法调用结束后,屏幕就会显示内容
renderView.compositeFrame();
// 如果启用了语义,则该方法将编译渲染对象的语义,传给系统用于辅助功能,如搜索等。
pipelineOwner.flushSemantics();
依据官方的drawFrame 文档的描述,绘制过程经历以下十个阶段:
1、动画阶段:handleBeginFrame
方法是用Window.onBeginFrame
注册的, 按注册顺序调用所有用 scheduleFrameCallback
注册的transientCallbacks
。其中包括所有驱动AnimationController
对象的Ticker
实例,也就是说此时所有的活动的Animation
对象的tick 在此刻回调。
2、Microtask
:在handleBeginFrame
返回后,任何被transientCallbacks
安排的微任务都会被运行。这通常包括来自Ticker
和AnimationController
的完成该帧的Future
回调。
在handleBeginFrame
之后,调用了Window.onDrawFrame
注册的handleDrawFrame
,它调用了所有的persistentCallbacks
,其中最值得注意的是drawFrame
这个方法,其过程如下。
3、构建阶段: 重建widget树中所有的dirty 元素(见 State.build). 请参阅 State.setState了解更多关于标记一个widget dirty的构建的细节。有关此步骤的更多信息,请参见 BuildOwner。
4、布局阶段: 系统中的所有标记 dirty的 RenderObject
都会被布局(参见 RenderObject.performLayout)。请参阅RenderObject.markNeedsLayout,了解关于标记一个对象脏以进行布局的更多细节。
5、合成位阶段:更新任何标记dirty的 RenderObject对象上的compositing bits 。请参见 RenderObject.markNeedsCompositingBitsUpdate。
6、绘制阶段:系统中所有标记dirty 的RenderObject
都会被重新绘制(见 RenderObject.paint)。这将生成Layer树。请参阅 RenderObject.markNeedsPaint,以获取有关将对象标记为dirty 的更多详细信息。
7、合成阶段:图层树被转化为一个Scene 并发送给GPU。
8、语义阶段:系统中所有标记 dirty的 RenderObject的语义都会被更新(见RenderObject.assembleSemanticsNode)。这将生成 SemanticsNode树。请参阅 RenderObject.markNeedsSemanticsUpdate以了解关于标记一个对象为dirty的语义的细节。
有关步骤4-8的更多详细信息,请参见PipelineOwner。
9、Widget
层的完成阶段:Widget
树已完成。这将导致在从Widget
树中删除的任何对象上调用State.dispose。有关更多详细信息,请参见 BuildOwner.finalizeTree。
10、调度层的最终化阶段。在drawFrame
返回后,handleDrawFrame
会调用postFrameCallbacks
回调(用addPostFrameCallback注册的)。
框架主要构建的三课树的根对象分别如下
根Widget:由我们自己创建,通过runApp
函数传入
void main() {
runApp(MyApp());
}
根Element:为RenderObjectToWidgetElement
类型,通过调用RenderObjectToWidgetAdapter
的createElement
创建
@override
RenderObjectToWidgetElement createElement() => RenderObjectToWidgetElement(this);
根RenderObject:为RenderView
类型。注意,由RenderObjectToWidgetAdapter
的createRenderObject
方法获取而非创建。
@override
RenderObjectWithChildMixin createRenderObject(BuildContext context) => container;
这里RenderObjectToWidgetAdapter
中的container
为外部传入的renderView
。
核心调用的时序图如下
本篇博客视频内容可访问B站链接 框架启动源码剖析 您觉得有帮助,别忘了点赞哦
如需要获取完整的Flutter全栈式开发课程,请访问以下地址
Flutter全栈式开发之Dart 编程指南
Flutter 全栈式开发指南