本文是『 深刻浅出 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 的刷新。