本文是『 深刻淺出 Flutter Framework 』系列文章的第六篇,詳細介紹了 PipelineOwner 在整個 Rendering Pipeline 中是如何協助『 RenderObject Tree 』、『 RendererBinding』以及『 Window』完成 UI 刷新。node
本文同時發表於個人我的博客git
本系列文章將深刻 Flutter Framework 內部逐步去分析其核心概念和流程,主要包括:github
PipelineOwner
在 Rendering Pipeline 中起到重要做用:markdown
簡單講,PipelineOwner
是『RenderObject Tree』與『RendererBinding』間的橋樑,在二者間起到溝通協調的做用。ide
如上圖:oop
RendererBinding
建立並持有PipelineOwner
實例,Code1-第8~12
行RendererBinding
會建立『RenderObject Tree』的根節點,即:RenderView,並將其賦值給PipelineOwner#rootNode
,Code1-第13~24
行PipelineOwner
實例 attach 到該節點上,即『RenderObject Tree』上全部結點共享同一個PipelineOwner
實例,Code2-第4
行1 // Code1-RendererBinding#init
2 // 代碼有刪減,下同
3 mixin RendererBinding {
4 @override
5 void initInstances() {
6 super.initInstances();
7 _instance = this;
8 _pipelineOwner = PipelineOwner(
9 onNeedVisualUpdate: ensureVisualUpdate,
10 onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
11 onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
12 );
13 initRenderView();
14 addPersistentFrameCallback(_handlePersistentFrameCallback);
15 }
16
17 void initRenderView() {
18 renderView = RenderView(configuration: createViewConfiguration(), window: window);
19 renderView.prepareInitialFrame();
20 }
21
22 set renderView(RenderView value) {
23 _pipelineOwner.rootNode = value;
24 }
複製代碼
// Code2-RenderObject#adoptChild
//
1 void adoptChild(covariant AbstractNode child) {
2 child._parent = this;
3 if (attached)
4 child.attach(_owner);
5 redepthChild(child);
6 }
7
8 void attach(covariant Object owner) {
9 _owner = owner;
10 }
複製代碼
RendererBinding
是 mixin,其背後真實的類是WidgetsFlutterBinding
post
如上所述,正常狀況下在 Flutter 運行過程當中只有一個PipelineOwner
實例,並由RendererBinding
持有,用於管理全部『 on-screen RenderObjects 』。 然而,若是有『 off-screen RenderObjects 』,則能夠建立新的PipelineOwner
實例來管理它們。 『on-screen PipelineOwner』與 『 off-screen PipelineOwner 』徹底獨立,後者須要建立者本身維護、驅動。ui
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable
如上,RendererBinding
要求其附屬類 mixinBindingBase
、ServicesBinding
、SchedulerBinding
、GestureBinding
、SemanticsBinding
以及HitTestable
,爲了描述方便,文本提到的RendererBinding
上的方法也可能來自於其餘幾個 Binding。this
Render Object 有4種『 Dirty State』 須要 PipelineOwner 去維護:spa
如上圖:
markNeedsLayout
方法,該方法會將當前 RenderObject 加入 PipelineOwner#_nodesNeedingLayout
或傳給父節點去處理;markNeedsCompositingBitsUpdate
方法,該方法會將當前 RenderObject 加入 PipelineOwner#_nodesNeedingCompositingBitsUpdate
或傳給父節點去處理;markNeedsPaint
方法,該方法會將當前 RenderObject 加入PipelineOwner#_nodesNeedingPaint
或傳給父節點處理;markNeedsSemanticsUpdate
方法,該方法會將當前 RenderObject 加入 PipelineOwner#_nodesNeedingSemantics
或傳給父節點去處理上述就是 PipelineOwner 不斷收集『 Dirty RenderObjects 』的過程。
RenderObject 內部的邏輯會在後續文章中詳細分析。
上述4個markNeeds*
方法,除了markNeedsCompositingBitsUpdate
,其餘方法最後都會調用PipelineOwner#requestVisualUpdate
。 之因此markNeedsCompositingBitsUpdate
不會調用PipelineOwner#requestVisualUpdate
,是由於其不會單獨出現,必定是伴隨其餘3個之一一塊兒出現的。
如上圖,隨着PipelineOwner#requestVisualUpdate
->RendererBinding#scheduleFrame
->Window#scheduleFrame
調用鏈,UI 須要刷新的信息最終傳遞到了 Engine 層。 具體講,Window#scheduleFrame
主要是向 Engine 請求在下一幀刷新時調用Window#onBeginFrame
以及Window#onDrawFrame
方法。
Window#onBeginFrame
、Window#onDrawFrame
本質上是 RendererBinding 向其注入的兩個回調(_handleBeginFrame
、_handleDrawFrame
):
// Code3-SchedulerBinding
//
1 void ensureFrameCallbacksRegistered() {
2 window.onBeginFrame ??= _handleBeginFrame;
3 window.onDrawFrame ??= _handleDrawFrame;
4 }
複製代碼
如上圖,Engine 在接收到 UI 須要更新後,在下一幀刷新時會調用
Window#onDrawFrame
,經過提早註冊好的PersistentFrameCallback
,最終調用到RendererBinding#drawFrame
方法:
// Code4-RendererBinding#drawFrame
//
1 void drawFrame() {
2 pipelineOwner.flushLayout();
3 pipelineOwner.flushCompositingBits();
4 pipelineOwner.flushPaint();
5 renderView.compositeFrame(); // this sends the bits to the GPU
6 pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
7 }
複製代碼
如上,RendererBinding#drawFrame
依次調用PipelineOwner
的flushLayout
、flushCompositingBits
、flushPaint
以及flushSemantics
方法,來處理對應狀態下的 RenderObject。
// Code5-PipelineOwner#flushLayout
//
1 void flushLayout() {
2 while (_nodesNeedingLayout.isNotEmpty) {
3 final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
4 _nodesNeedingLayout = <RenderObject>[];
5 for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
6 if (node._needsLayout && node.owner == this)
7 node._layoutWithoutResize();
8 }
9 }
10 }
複製代碼
首先,PipelineOwner
對於收集到的『 Needing Layout RenderObjects 』按其在『 RenderObject Tree 』上的深度升序排序,主要是爲了不子節點重複 Layout (由於父節點 layout 時,也會遞歸地對子樹進行 layout); 其次,對排好序的且知足條件的 RenderObjects 依次調用_layoutWithoutResize
來執行 layout 操做。
在父節點 layout 完成時,其全部子節點也 layout 完成,它們的
_needsLayout
標誌會被置爲flase
,所以在 Code5 中須要第6
行的判斷,避免重複 layout。
// Code6-PipelineOwner#flushCompositingBits
//
1 void flushCompositingBits() {
2 _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
3 for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
4 if (node._needsCompositingBitsUpdate && node.owner == this)
5 node._updateCompositingBits();
6 }
7 _nodesNeedingCompositingBitsUpdate.clear();
8 }
複製代碼
同理,先對『 Needing Compositing Bits RenderObjects 』排序,再調用RenderObjects#_updateCompositingBits
// Code7-PipelineOwner#flushPaint
//
1 void flushPaint() {
2 final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
3 _nodesNeedingPaint = <RenderObject>[];
4 // Sort the dirty nodes in reverse order (deepest first).
5 for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
6 if (node._needsPaint && node.owner == this) {
7 if (node._layer.attached) {
8 PaintingContext.repaintCompositedChild(node);
9 }
10 }
11 }
12 }
複製代碼
對於 Paint 操做來講,父節點須要用到子節點繪製的結果,故子節點須要先於父節點被繪製。 所以,不一樣於前兩個 flush 操做,此時須要對『 Needing Paint RenderObjects 』按深度降序排序。 以下圖,在深刻淺出 Flutter Framework 之 PaintingContext一文中詳細分析了從PipelineOwner#flushPaint
到PaintingContext
內部操做的過程,在此再也不贅述。
// Code8-PipelineOwner#flushSemantics
//
1 void flushSemantics() {
2 final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
3 ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
4 _nodesNeedingSemantics.clear();
5 for (RenderObject node in nodesToProcess) {
6 if (node._needsSemanticsUpdate && node.owner == this)
7 node._updateSemantics();
8 }
9 _semanticsOwner.sendSemanticsUpdate();
10 }
複製代碼
Flush Semantics 所作操做與 Flush Layout 徹底類似,再也不贅述。
至此,PipelineOwner 相關的內容就介紹完了。
PipelineOwner 做爲『 RenderObject Tree』與『 RendererBinding/Window』間的溝通協調橋樑,在整個 Rendering Pipeline 中起到重要做用。 在 Flutter 應用生命週期內,不斷收集『 Dirty RenderObjects 』並及時通知 Engine。 在幀刷新時,經過來自 RendererBinding 的回調依次處理收集到的:
最終完成 UI 的刷新。