https://book.flutterchina.club/chapter14/flutter_app_startup.html
setState(() {...})
- _element.markNeedsBuild() :将元素标记为脏,并将其添加到全局窗口小部件列表中,以便在下一帧中重建。
- owner.scheduleBuildFor(this) :将元素添加到脏元素列表 _dirtyElements 中,以便在[WidgetsBinding.drawFrame]调用[buildScope]时重建它。
- onBuildScheduled() . flutter/packages/flutter/lib/src/widgets/framework.dart
- ensureVisualUpdate() . flutter/packages/flutter/lib/src/widgets/binding.dart
- scheduleFrame() . flutter/packages/flutter/lib/src/scheduler/binding.dart
- window.scheduleFrame() 进入 native code
https://sourcegraph.com/github.com/flutter/engine/-/blob/lib/ui/window/window.cc#L375
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {natives->Register({{"Window_defaultRouteName", DefaultRouteName, 1, true},{"Window_scheduleFrame", ScheduleFrame, 1, true},{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},{"Window_render", Render, 2, true},{"Window_updateSemantics", UpdateSemantics, 2, true},{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},{"Window_reportUnhandledException", ReportUnhandledException, 2, true},{"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},});}
Engine::ScheduleFrame:https://sourcegraph.com/github.com/flutter/engine@master/-/blob/shell/common/engine.cc#L414
RequestFramehttps://sourcegraph.com/github.com/flutter/engine@master/-/blob/shell/common/animator.cc#L194
AwaitVSync:https://sourcegraph.com/github.com/flutter/engine@master/-/blob/shell/common/animator.cc#L221
BeginFrame:https://sourcegraph.com/github.com/flutter/engine@master/-/blob/shell/common/animator.cc#L234
Signal() https://sourcegraph.com/github.com/flutter/engine@master/-/blob/shell/common/animator.cc#L109
dispatch_semaphore_signal https://sourcegraph.com/github.com/flutter/engine@master/-/blob/fml/synchronization/semaphore.cc#L42
Dispatch Semaphore是持有计数的信号,该信号是多线程编程中的计数类型信号。信号类似于过马路时的手旗,可以通过时举起手旗,不可通过时放下手旗。而在Dispatch Semaphore中使用了计数来实现该功能。计数为0时等待,计数为1或者大于1时放行。
信号量的使用比较简单,主要就三个API:create、wait和signal。
使用篇
dispatch_semaphore_create可以生成信号量,参数value是信号量计数的初始值;dispatch_semaphore_wait会让信号量值减一,当信号量值为0时会等待(直到超时),否则正常执行;dispatch_semaphore_signal会让信号量值加一,如果有通过dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值增加的线程,会由系统唤醒最先等待的线程执行。
https://xiaozhuanlan.com/topic/4365017982
bool RuntimeController::BeginFrame(fml::TimePoint frame_time) {if (auto* window = GetWindowIfAvailable()) {window->BeginFrame(frame_time);return true;}return false;}
https://sourcegraph.com/github.com/flutter/engine/-/blob/lib/ui/window/window.cc#L326
void Window::BeginFrame(fml::TimePoint frameTime) {std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();if (!dart_state)return;tonic::DartState::Scope scope(dart_state);int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame",{Dart_NewInteger(microseconds),}));UIDartState::Current()->FlushMicrotasksNow();tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {}));}
https://sourcegraph.com/github.com/flutter/engine/-/blob/lib/ui/hooks.dart#L175:12
pragma: https://github.com/dart-lang/sdk/blob/master/runtime/docs/pragmas.md
@pragma('vm:entry-point')注解,其核心逻辑在于Tree-Shaking。在AOT(ahead of time)编译下,如果不能被应用主入口(main)最终可能调到,那么将被视为无用代码而丢弃。AOP代码因为其注入逻辑的无侵入性,显然是不会被main调到的,因此需要此注解告诉编译器不要丢弃这段逻辑
@pragma('vm:entry-point')// ignore: unused_elementvoid _beginFrame(int microseconds) {_invoke1<Duration>(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds));}
@pragma('vm:entry-point')// ignore: unused_elementvoid _drawFrame() {_invoke(window.onDrawFrame, window._onDrawFrameZone);}
mixin SchedulerBinding on BindingBase, ServicesBinding {void initInstances() {super.initInstances();_instance = this;window.onBeginFrame = _handleBeginFrame;window.onDrawFrame = _handleDrawFrame;}
handleBeginFrame
https://sourcegraph.com/github.com/flutter/flutter/-/blob/packages/flutter/lib/src/scheduler/binding.dart#L900:3
handleDrawFrame
https://sourcegraph.com/github.com/flutter/flutter/-/blob/packages/flutter/lib/src/scheduler/binding.dart#L951:8
FrameCallback:SchedulerBinding 类中有三个FrameCallback回调队列, 在一次绘制过程中,这三个回调队列会放在不同时机被执行:
- transientCallbacks:用于存放一些临时回调,一般存放动画回调。可以通过
SchedulerBinding.instance.scheduleFrameCallback添加回调。Triggered by the system’s [Window.onBeginFrame] - persistentCallbacks:用于存放一些持久的回调,不能在此类回调中再请求新的绘制帧,持久回调一经注册则不能移除。
SchedulerBinding.instance.addPersitentFrameCallback(),这个回调中处理了布局与绘制工作。Triggered by the system’s [Window.onDrawFrame] - postFrameCallbacks:在Frame结束时只会被调用一次,调用后会被系统移除,可由
SchedulerBinding.instance.addPostFrameCallback()注册,注意,不要在此类回调中再触发新的Frame,这可以会导致循环刷新。which are run after persistent callbacks, just /// before returning from the [Window.onDrawFrame] callback
void handleDrawFrame() {try {// PERSISTENT FRAME CALLBACKS_schedulerPhase = SchedulerPhase.persistentCallbacks;for (FrameCallback callback in _persistentCallbacks)_invokeFrameCallback(callback, _currentFrameTimeStamp);// POST-FRAME CALLBACKS_schedulerPhase = SchedulerPhase.postFrameCallbacks;final List<FrameCallback> localPostFrameCallbacks =List<FrameCallback>.from(_postFrameCallbacks);_postFrameCallbacks.clear();for (FrameCallback callback in localPostFrameCallbacks)_invokeFrameCallback(callback, _currentFrameTimeStamp);} finally {}}
addPersistentFrameCallback
https://sourcegraph.com/github.com/flutter/flutter/-/blob/packages/flutter/lib/src/scheduler/binding.dart#L581
final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];void addPersistentFrameCallback(FrameCallback callback) {_persistentCallbacks.add(callback);}
RenderingBinding.drawFrame
mixin RendererBinding on BindingBase, SchedulerBinding, SemanticsBinding {@overridevoid initInstances() {super.initInstances();addPersistentFrameCallback(_handlePersistentFrameCallback);}void _handlePersistentFrameCallback(Duration timeStamp) {drawFrame();}@protectedvoid drawFrame() {assert(renderView != null);pipelineOwner.flushLayout(); // update dirty RenderObject layoutpipelineOwner.flushCompositingBits();pipelineOwner.flushPaint();renderView.compositeFrame(); // call ui.window.render this sends the bits to the GPUpipelineOwner.flushSemantics(); // this also sends the semantics to the OS.}}
WidgetBinding.drawFrame (super.drawFrame RenderingBinding:drawFrame)
https://sourcegraph.com/github.com/flutter/flutter/-/blob/packages/flutter/lib/src/widgets/binding.dart
@overridevoid drawFrame() {buildOwner.buildScope(renderViewElement);super.drawFrame(); // RenderBinding.drawFrame()}
- buildScope()
- rebuild()
- performRebuild()
- drawFrame()
- window.render()
void buildScope(Element context, [ VoidCallback callback ]) {int dirtyCount = _dirtyElements.length;int index = 0;while (index < dirtyCount) {_dirtyElements[index].rebuild();index += 1;}Timeline.finishSync();}
- Flutter 框架会按执行一系列动作: 动画(Animate)、构建(Build)、布局(Layout)和 绘制(Paint),最终生成一个场景(Scene)之后送往底层,由GPU绘制到屏幕上.
https://juejin.im/post/5c876a58f265da2db3058e2f
vsync
vsync(垂直同步)指计算机操作系统的一个信号功能。Android系统每隔 16ms 发出VSYNC信号,触发GPU对UI进行渲染。如果你的某个操作花费时间是24ms,系统在得到VSYNC信号的时候由于还没有准备好,就无法进行更新任何内容,那么用户在32ms内看到的会是同一帧画面(卡顿现象),即丢帧现象。https://www.jianshu.com/p/0ee4b0124e56
