之前介绍的渲染流水线可以知道,这个过程大致可以分为两段操作。第一段是从`State.setState()`到去engine那里请求一帧,第二段就是Vsync信号到来以后渲染流水线开始重建新的一帧最后送入engine去显示。
## setState
~~~
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element.markNeedsBuild();
}
~~~
~~~
void markNeedsBuild() {
if (!_active)
return;
if (dirty)
return;
_dirty = true;
owner.scheduleBuildFor(this);
}
~~~
这个`BuildOwner`我们之前介绍过,它的实例是在`WidgetsBinding`初始化的时候构建的。每个`Element`的都会持有`BuildOwner`的引用
~~~
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements.add(element);
element._inDirtyList = true;
}
~~~
`BuildOwner`会维护一个`_dirtyElements`列表,所有被标记为“脏”(dirty)的`element`都会被添加进去。在此之前会调用`onBuildScheduled()`。这个函数是`WidgetsBinding`初始化的时候设置给`BuildOwner`的,对应的是`WidgetsBinding._handleBuildScheduled()`。
~~~
void _handleBuildScheduled() {
ensureVisualUpdate();
}
~~~
这里会调用到`ensureVisualUpdate()`。这个函数定义在`SchedulerBinding`里的
~~~
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
~~~
函数`ensureVisualUpdate()`会判断当前调度所处的状态,如果是在`idle`(空闲)或者`postFrameCallbacks`运行状态则调用`scheduleFrame()`。其他状态则直接返回。下面这三个状态正是渲染流水线运行的时候。
~~~
void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
window.scheduleFrame();
_hasScheduledFrame = true;
}
~~~
`_hasScheduledFrame`标志位是为了避免重复请求。
`_framesEnabled`是代表当前app的状态,或者说其所处的生命周期是否允许刷新界面。
## vsync信号
Vsync信号到来之后,engin会按顺序回调`window`的两个回调函数:`onBeginFrame()`和`onDrawFrame()`。这两个回调是`SchedulerBinding`初始化的时候设置给`window`的。对应的是`SchedulerBinding.handleBeginFrame()`和`SchedulerBinding.handleDrawFrame()`。
## onBeginFrame
这个回调会直接走到`SchedulerBinding.handleBeginFrame()`。
~~~
void handleBeginFrame(Duration rawTimeStamp) {
...
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
_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;
}
}
~~~
这个函数主要是在依次回调“Transient”回调函数,这些回调函数是在调度之前设置在`SchedulerBinding`里的,这里的“Transient”意思是临时的,或者说是一次性的。原因是这些回调函数只会被调用一次。注意看代码里`_transientCallbacks`被置为空`Map`了。如果想在下一帧再次调用的话需要提前重新设置回调。这些回调主要和动画有关系。也就是渲染流水线里的第一阶段,动画(Animate)阶段。
在运行回调之前`_schedulerPhase`的状态被设置为`SchedulerPhase.transientCallbacks`。回调处理完以后状态更新至`SchedulerPhase.midFrameMicrotasks`意思是接下来会处理微任务队列。处理完微任务以后,engine会接着回调`onDrawFrame()`。
## onDrawFrame
这个回调会直接走到`SchedulerBinding.handleDrawFrame()`。
~~~
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 {
_schedulerPhase = SchedulerPhase.idle;
_currentFrameTimeStamp = null;
}
}
~~~
在`handleDrawFrame`里按顺序处理了两类回调,一类叫“Persistent”回调,另一类叫“Post-Frame”回调。
“Persistent”字面意思是永久的。这类回调一旦注册以后是不能取消的。主要用来驱动渲染流水线。渲染流水线的构建(build),布局(layout)和绘制(paint)阶段都是在其中一个回调里的。
“Post-Frame”回调主要是在新帧渲染完成以后的一类调用,此类回调只会被调用一次。
在运行“Persistent”回调之前`_schedulerPhase`状态变为`SchedulerPhase.persistentCallbacks`。在运行“Post-Frame”回调之前`_schedulerPhase`状态变为`SchedulerPhase.postFrameCallbacks`。最终状态变为`SchedulerPhase.idle`。
这里我们主要关注一个“Persistent”回调:`WidgetsBinding.drawFrame()`。这个函数是在`RendererBinding`初始化的时候加入到“Persistent”回调的。
~~~
void drawFrame() {
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
...
}
}
~~~
这里首先会调用`buildOwner.buildScope(renderViewElement)`。其入参`renderViewElement`是element tree的根节点。此时渲染流水线就进入了构建(build)阶段。接下来调用了`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.
}
~~~
可以看出渲染流水线的接力棒传到了`pipelineOwner`的手里,渲染流水线就进入了布局(layout)阶段和绘制(paint)阶段。关于最后这两个阶段本篇不做详细介绍。这里大家只要知道绘制完成以后Flutter框架最终会调用`window.render(scene)`将新帧的数据送入engine显示到屏幕。
最后调用`buildOwner.finalizeTree();`。这个函数的作用是清理不再需要的`Element`节点。在element tree更新以后可能有些节点就不再需要挂载在树上了,在`finalizeTree()`的时候会将这些节点及其子节点unmount。
### 总结
1. State.setState -- Element.markNeedsBuild --
BuildOwner.scheduleBuildFor
2. BuildOwner 会维护一个`_dirtyElements`列表,scheduleBuildFor后所有被标记为“脏”(dirty)的`element`都会被添加进去
3. BuildOwner.scheduleBuildFor -- BuildOwner.onBuildScheduled -- WidgetsBinding.\_handleBuildScheduled -- SchedulerBinding.ensureVisualUpdate -- SchedulerBinding.scheduleFrame -- window.scheduleFrame()
4. window.onBeginFrame -- SchedulerBinding.handleBeginFrame -- 回调\_transientCallbacks里面回调函数 (与动画相关)
5. window.onDrawFrame -- SchedulerBinding.handleDrawFrame()
6. handleDrawFrame内处理了两类回调,一类叫“Persistent”回调,另一类叫“Post-Frame”回调。“Persistent”字面意思是永久的。这类回调一旦注册以后是不能取消的。主要用来驱动渲染流水线。渲染流水线的构建(build),布局(layout)和绘制(paint)阶段都是在其中一个回调里的。
“Post-Frame”回调主要是在新帧渲染完成以后的一类调用,此类回调只会被调用一次。
7. 主要关注一个“Persistent”回调:`WidgetsBinding.drawFrame()`。这个函数是在`RendererBinding`初始化的时候加入到“Persistent”回调的。
8. drawFrame中
* 首先调用buildOwner.buildScope(renderViewElement);此时构建(build)阶段
* 接下来调用了`super.drawFrame()`。这个函数定义在`RendererBinding`中。渲染流水线就进入了布局(layout)阶段和绘制(paint)阶段。
* 最后调用`buildOwner.finalizeTree();`。这个函数的作用是清理不再需要的`Element`节点。在element tree更新以后可能有些节点就不再需要挂载在树上了