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_element
void _beginFrame(int microseconds) {
_invoke1<Duration>(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds));
}
@pragma('vm:entry-point')
// ignore: unused_element
void _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 {
@override
void initInstances() {
super.initInstances();
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout(); // update dirty RenderObject layout
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
renderView.compositeFrame(); // call ui.window.render this sends the bits to the GPU
pipelineOwner.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
@override
void 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