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 信號的回調,而是直接繪製。