Flutter Framework层UI绘制的入口(一)
本文记录自己学习 Flutter 绘制所见源码,并不是全面详细的解析 Blog。
Flutter 源码环境:1.22.1
Flutter 官方提供一张 Vsync 信号使 UI 线程和 GPU 线程相互协调渲染界面,如图所示:

由上图可知,UI 线程只负责 Dart 层的视图数据的处理绘制,并将处理后的尽快数据提供给 GPU。
Flutter Framework 绘制层
初始化注册以及响应
在 Flutter 中,对 Vsync 信号的注册,处理响应 Vsync 到来的过程中,底层会调用到 window.onBeginFrame()和 onDrawFrame()这两个方法,对应 Dart 层就_onBeginFrame,_onDrawFrame。
1.2 window
sky_engine/lib/ui/window.dart
class Window {Window._() {...}FrameCallback? get onBeginFrame => _onBeginFrame;FrameCallback? _onBeginFrame;set onBeginFrame(FrameCallback? callback) {_onBeginFrame = callback;...}VoidCallback? get onDrawFrame => _onDrawFrame;VoidCallback? _onDrawFrame;Zone _onDrawFrameZone = Zone.root;set onDrawFrame(VoidCallback? callback) {_onDrawFrame = callback;...}}
onBeginFrame,onDrawFrame 实际持有者是_onBeginFrame,_onDrawFrame。那它又是怎么被初始化赋值的呢?
1.1 初始化
我们从入口方法 main(),一步步来看。
1.1.1 main()
void main() {runApp(MyApp());}
1.1.2 runApp()
flutter/lib/src/widgets/binding.dart
void runApp(Widget app) {WidgetsFlutterBinding.ensureInitialized()..scheduleAttachRootWidget(app)..scheduleWarmUpFrame();}
1.1.3 scheduleAttachRootWidget()
flutter/lib/src/widgets/binding.dart
@protectedvoid 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 的过程中,会实例化一个_renderViewElement,并把应用传过来的 rootWidget 作为根节点加入树中。
1.1.4 attachToRenderTree()
flutter/lib/src/widgets/binding.dart
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 是空的,那么会创建一个新的,调用 ensureVisualUpdate(),否则标识更新。
再继续看SchedulerBinding.instance.ensureVisualUpdate()方法。
flutter/lib/src/scheduler/binding.dart
void ensureVisualUpdate() {switch (schedulerPhase) {case SchedulerPhase.idle:case SchedulerPhase.postFrameCallbacks:scheduleFrame();return;case SchedulerPhase.transientCallbacks:case SchedulerPhase.midFrameMicrotasks:case SchedulerPhase.persistentCallbacks:return;}}...void scheduleFrame() {...//确保有注册监听回调ensureFrameCallbacksRegistered();//注册信号window.scheduleFrame();_hasScheduledFrame = true;}@protectedvoid ensureFrameCallbacksRegistered()//初始化赋值window.onBeginFrame ??= _handleBeginFrame;window.onDrawFrame ??= _handleDrawFrame;}
终于在 ensureFrameCallbacksRegistered() 方法中看到了对_onBeginFrame,_onDrawFrame 的赋值。
继续看看_handleBeginFrame,_handleDrawFrame 都做了些什么?
1.3 _handleBeginFrame
flutter/lib/src/scheduler/binding.dart
void _handleBeginFrame(Duration rawTimeStamp) {if (_warmUpFrame) {assert(!_ignoreNextEngineDrawFrame);_ignoreNextEngineDrawFrame = true;return;}//调用 1.3.1handleBeginFrame(rawTimeStamp);}
1.3.1 handleBeginFrame
flutter/lib/src/scheduler/binding.dart
void handleBeginFrame(Duration? rawTimeStamp) {Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);_firstRawTimeStampInEpoch ??= rawTimeStamp;_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);if (rawTimeStamp != null)_lastRawTimeStamp = rawTimeStamp;...//此时阶段等于SchedulerPhase.idle;因为初始值就是idle_hasScheduledFrame = false;try {// TRANSIENT FRAME CALLBACKSTimeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);_schedulerPhase = SchedulerPhase.transientCallbacks;final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;_transientCallbacks = <int, _FrameCallbackEntry>{};//执行动画回调方法callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {if (!_removedIds.contains(id))_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);});_removedIds.clear();} finally {_schedulerPhase = SchedulerPhase.midFrameMicrotasks;}}
该方法主要就是 遍历_transientCallbacks,执行对应的回调方法,_transientCallbacks 链表可以通过scheduleFrameCallback()/cancelFrameCallbackWithId()方法来添加和删除回调。
1.4 _handleDrawFrame
flutter/lib/src/scheduler/binding.dart
void _handleDrawFrame() {if (_ignoreNextEngineDrawFrame) {_ignoreNextEngineDrawFrame = false;return;}//调用1.4.1handleDrawFrame();}
1.4.1 handleDrawFrame
flutter/lib/src/scheduler/binding.dart
void handleDrawFrame() {assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);Timeline.finishSync(); // end the "Animate" phase 标识结束"Animate"阶段try {// PERSISTENT FRAME CALLBACKS 执行PERSISTENT FRAME回调_schedulerPhase = SchedulerPhase.persistentCallbacks;for (final FrameCallback callback in _persistentCallbacks)//调用 1.4.2_invokeFrameCallback(callback, _currentFrameTimeStamp!);// POST-FRAME CALLBACKS 执行POST-FRAME回调_schedulerPhase = SchedulerPhase.postFrameCallbacks;final List<FrameCallback> localPostFrameCallbacks =List<FrameCallback>.from(_postFrameCallbacks);_postFrameCallbacks.clear();for (final FrameCallback callback in localPostFrameCallbacks)_invokeFrameCallback(callback, _currentFrameTimeStamp!);} finally {_schedulerPhase = SchedulerPhase.idle;Timeline.finishSync(); // end the Frame 标识结束”Frame“阶段_currentFrameTimeStamp = null;}}
该方法的主要功能:
遍历_persistentCallbacks,执行相应的回调方法,_persistentCallbacks 通过 addPersistentFrameCallback()添加成员,一旦注册后不可移除,后续每一次 frame 回调都会执行。
遍历_postFrameCallbacks,执行相应的回调方法,_postFrameCallbacks 通过 addPostFrameCallback()添加成员,handleDrawFrame()执行完成后会清空_postFrameCallbacks 链表。
1.4.2 _invokeFrameCallback
flutter/lib/src/scheduler/binding.dart
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) {...try {//回调callback(timeStamp);} catch (exception, exceptionStack) {...}}}
callback 执行的是 _persistentCallbacks 或者_postFrameCallbacks 成员中的回调。看看这些回调是怎么在初始化的时候加进去的。
1.5 WidgetsBinding.initInstances
在调用 runApp 后会调用到 ensureInitialized()获取 WidgetsBinding 的实例对象 _instance,
_instance 在 initInstances() 中得以初始化。
flutter/lib/src/widgets/binding.dart
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {@overridevoid initInstances() {//调用1.5.1super.initInstances();_instance = this;//其他初始化...}}
runApp 过程会有 WidgetsFlutterBinding 初始化过程,调用WidgetsBinding 的 initInstances(),因为 WidgetsBinding 混入(mixin) RendererBinding,所以 super.initInstances()也会调用到 RendererBinding 的 initInstances()方法中。
1.5.1 RendererBinding
flutter/lib/src/rendering/binding.dart
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {void initInstances() {super.initInstances();_instance = this;//初始化_pipelineOwner_pipelineOwner = PipelineOwner(onNeedVisualUpdate: ensureVisualUpdate,onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,);//注册一些回调window..onMetricsChanged = handleMetricsChanged..onTextScaleFactorChanged = handleTextScaleFactorChanged..onPlatformBrightnessChanged = handlePlatformBrightnessChanged..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged..onSemanticsAction = _handleSemanticsAction;//初始化renderViewinitRenderView();_handleSemanticsEnabledChanged();assert(renderView != null);//终于在这看到了对_persistentCallbacks 的注册赋值//调用1.5.2addPersistentFrameCallback(_handlePersistentFrameCallback);initMouseTracker();}void _handlePersistentFrameCallback(Duration timeStamp) {//调用 1.6drawFrame();//该方法添加了_postFrameCallbacks链表回调成员。_scheduleMouseTrackerUpdate();}}
1.5.2 addPersistentFrameCallback
flutter/lib/src/rendering/binding.dart
void addPersistentFrameCallback(FrameCallback callback) {//添加回调_persistentCallbacks.add(callback);}
1.6 drawFrame
/flutter/lib/src/widgets/binding.dart
//此方法由[handleDrawFrame]调用,当需要布置和绘制框架时,引擎会自动调用该方法。void drawFrame() {...try {if (renderViewElement != null)buildOwner.buildScope(renderViewElement);super.drawFrame();buildOwner.finalizeTree();} finally {...}...}
