2、Widget、Element、RenderObjectapp
4、build 流程分析ide
7、Paint 绘制(2)post
第一章说起到在 Flutter 中,是基于 Vsync 垂直信号的机制来协调图像数据的生成,那在 flutter 中,是怎么触发监听 vsync 信号的呢this
setState 方法 是咱们在写UI 组件时经常使用的方法,用于UI界面数据的更新spa
(1)调用 setState 方法
@protected
void setState(VoidCallback fn) {
_element.markNeedsBuild();
}
复制代码
(2)markNeedsBuild
void markNeedsBuild() {
if (dirty)
return;
_dirty = true; // 将元素标记为"脏元素"
owner.scheduleBuildFor(this);
}
复制代码
将元素标记为"脏元素"( flutter 在更新触发从新渲染时,只会将标脏了的元素从新绘制渲)同时调用 BuildOwner 的 scheduleBuildFor 方法
(3)scheduleBuildFor
void scheduleBuildFor(Element element) {
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements.add(element);
element._inDirtyList = true;
}
复制代码
这个方法中,将该元素添加到脏元素集合中,同时调用 onBuildScheduled 方法
(4) onBuildScheduled
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
buildOwner.onBuildScheduled = _handleBuildScheduled;
}
复制代码
onBuildScheduled 方法是在 flutter 初始化时进行绑定的一个方法
(5) _handleBuildScheduled
void _handleBuildScheduled() {
ensureVisualUpdate();
}
复制代码
(6) ensureVisualUpdate判断当前调度所处的状态,若是是 idle(空闲)或 postFrameCallbacks 时调用 scheduleFrame
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
复制代码
(7) scheduleFrame
void scheduleFrame() {
window.scheduleFrame();
}
复制代码
(8)window.scheduleFrame
void scheduleFrame() native 'Window_scheduleFrame';
复制代码
scheduleFrame 是底部 dart engine 底层的一个方法,这个方法用于 注册 vsync 信号的监听。因而可知,setState 方法的主要原理是,将当前元素标脏,同时触发 vsync 信号,以便在下次 vsync 信号回调时,完成这些脏元素的更新。
(1) Window
Window Flutter Framework链接宿主操做系统的接口, 在 Window 类 中定义了Vsync 信号的回调处理
VoidCallback get onDrawFrame => _onDrawFrame;
复制代码
(2) SchedulerBinding
window.onDrawFrame = _handleDrawFrame;
复制代码
onDrawFrame 方法在 SchedulerBinding初始化时进行了从新指向
(3) _handleDrawFrame
void _handleDrawFrame() {
handleDrawFrame();
}
复制代码
(4) handleDrawFrame
void handleDrawFrame() {
_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);
}
复制代码
在这个方法中,主要是执行一些回调集合,其中
persistentCallbacks:用于存放一些持久的回调,以下代码所示SchedulerBinding.instance.addPersitentFrameCallback(),这个回调中处理了布局与绘制工做,即 调用执行 build/layout/paint 流水线工做的地方,
postFrameCallbacks:在Frame结束时只会被调用一次,调用后会被系统移除,可由 SchedulerBinding.instance.addPostFrameCallback() 注册
(5)persistentCallbacks --> RendererBinding
void initInstances() {
super.initInstances();
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
复制代码
(6)_handlePersistentFrameCallback
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
复制代码
(7)WidgetsBinding 中的 drawFrame
void drawFrame() {
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
}
}
复制代码
(8) super.drawFrame()(RendererBinding)
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}
复制代码
mixin widgetBinding 以 RenderingBinding 为基础,所以会覆盖 RendererBinding 的drawFrame 函数,该方法执行的操做,其实就是 build -> layout -> paint -> composite
前面分析了 setState 方法的工做原理,该方法主要是更新数据时调用,但 app 启动过程当中,并无触发 setState 方法,整个 app 首帧是怎么渲染的呢?
(1) runApp
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
复制代码
ensureInitialized 主要时实现 各类 Binding 的初始化,例如 SchedulerBinding, RendererBinding, WidgetsBinding 等, attachRootWidget 方法是完成整个 widget UI 树的构建挂载
(2)scheduleWarmUpFrame
void scheduleWarmUpFrame() {
handleBeginFrame(null);
handleDrawFrame();
if (hadScheduledFrame)
scheduleFrame();
}
复制代码
在完成UI 树的挂载以后,调用了 scheduleWarmUpFrame, 这个方法主要是调用了 handleBeginFrame 和 handleDrawFrame, 其中 handleBeginFrame 主要是 处理 transientCallbacks, 这个方法通常存放动画回调。能够经过SchedulerBinding.instance.scheduleFrameCallback 添加回调, handleDrawFrame 方法则是调用 drawFrame 方法完成首帧的绘制
(3) handleBeginFrame
void handleBeginFrame(Duration rawTimeStamp) {
_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);
});
}
复制代码
因而可知,首帧的绘制并无等待 Vsync 信号的回调,而是直接绘制。