之前介紹的渲染流水線可以知道,這個過程大致可以分為兩段操作。第一段是從`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更新以后可能有些節點就不再需要掛載在樹上了