本文是『 深入浅出 Flutter Framework 』系列文章的第六篇,详细介绍了 PipelineOwner 在整个 Rendering Pipeline 中是如何协助『 RenderObject Tree 』、『 RendererBinding』以及『 Window』完成 UI 刷新。
©原创文章,转载请注明出处!
本系列文章将深入 Flutter Framework 内部逐步去分析其核心概念和流程,主要包括:
『 深入浅出 Flutter Framework 之 Widget 』
『 深入浅出 Flutter Framework 之 BuildOwner 』
『 深入浅出 Flutter Framework 之 Element 』
『 深入浅出 Flutter Framework 之 PaintingContext 』
『 深入浅出 Flutter Framework 之 Layer 』
『 深入浅出 Flutter Framework 之 PipelineOwner 』
『 深入浅出 Flutter Framework 之 RenderObejct 』
『 深入浅出 Flutter Framework 之自定义渲染型 Widget 』
Overview
PipelineOwner在 Rendering Pipeline 中起到重要作用:
- 随着 UI 的变化而不断收集『 Dirty Render Objects 』
- 随之驱动 Rendering Pipeline 刷新 UI
简单讲,PipelineOwner是『RenderObject Tree』与『RendererBinding』间的桥梁,在两者间起到沟通协调的作用。
关系
如上图:
RendererBinding创建并持有PipelineOwner实例,Code1-第8~12行- 同时,
RendererBinding会创建『RenderObject Tree』的根节点,即:RenderView,并将其赋值给PipelineOwner#rootNode,Code1-第13~24行 - 在『RenderObject Tree』构建过程中,每插入一个新节点,就会将
PipelineOwner实例 attach 到该节点上,即『RenderObject Tree』上所有结点共享同一个PipelineOwner实例,Code2-第4行
1 | // 代码有删减,下同 |
1 | void adoptChild(covariant AbstractNode child) { |
RendererBinding是 mixin,其背后真实的类是WidgetsFlutterBinding
如上所述,正常情况下在 Flutter 运行过程中只有一个PipelineOwner实例,并由RendererBinding持有,用于管理所有『 on-screen RenderObjects 』。
然而,如果有『 off-screen RenderObjects 』,则可以创建新的PipelineOwner实例来管理它们。
『on-screen PipelineOwner』与 『 off-screen PipelineOwner 』完全独立,后者需要创建者自己维护、驱动。
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable
如上,RendererBinding要求其附属类 mixinBindingBase、ServicesBinding、SchedulerBinding、GestureBinding、SemanticsBinding以及HitTestable,为了描述方便,文本提到的RendererBinding上的方法也可能来自于其他几个 Binding。
驱动
Dirty RenderObjects
Render Object 有4种『 Dirty State』 需要 PipelineOwner 去维护:
- Needing Layout:Render Obejct 需要重新 layout
- Needing Compositing Bits Update:Render Obejct 合成标志位(Compositing)有变化
- Needing Paint:Render Obejct 需要重新绘制
- Needing Semantics:Render Object 辅助信息有变化

如上图:
- 当 RenderObject 需要重新 layout 时,调用
markNeedsLayout方法,该方法会将当前 RenderObject 加入PipelineOwner#_nodesNeedingLayout或传给父节点去处理; - 当 RenderObject 的 Compositing Bits 有变化时,调用
markNeedsCompositingBitsUpdate方法,该方法会将当前 RenderObject 加入PipelineOwner#_nodesNeedingCompositingBitsUpdate或传给父节点去处理; - 当 RenderObject 需要重新 paint 时,调用
markNeedsPaint方法,该方法会将当前 RenderObject 加入PipelineOwner#_nodesNeedingPaint或传给父节点处理; - 当 RenderObject 的辅助信息(Semantics)有变化时,调用
markNeedsSemanticsUpdate方法,该方法会将当前 RenderObject 加入PipelineOwner#_nodesNeedingSemantics或传给父节点去处理
上述就是 PipelineOwner 不断收集『 Dirty RenderObjects 』的过程。
RenderObject 内部的逻辑会在后续文章中详细分析。
Request Visual Update
上述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
2
3
4 void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
Handle Draw Frame

如上图,Engine 在接收到 UI 需要更新后,在下一帧刷新时会调用Window#onDrawFrame,通过提前注册好的PersistentFrameCallback,最终调用到RendererBinding#drawFrame方法:1
2
3
4
5
6
7void 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.
}
如上,RendererBinding#drawFrame依次调用PipelineOwner的flushLayout、flushCompositingBits、flushPaint以及flushSemantics方法,来处理对应状态下的 RenderObject。
Flush Layout
1 | void flushLayout() { |
首先,PipelineOwner对于收集到的『 Needing Layout RenderObjects 』按其在『 RenderObject Tree 』上的深度升序排序,主要是为了避免子节点重复 Layout (因为父节点 layout 时,也会递归地对子树进行 layout);
其次,对排好序的且满足条件的 RenderObjects 依次调用_layoutWithoutResize来执行 layout 操作。
在父节点 layout 完成时,其所有子节点也 layout 完成,它们的
_needsLayout标志会被置为flase,因此在 Code5 中需要第6行的判断,避免重复 layout。
Flush Compositing Bits
1 | void flushCompositingBits() { |
同理,先对『 Needing Compositing Bits RenderObjects 』排序,再调用RenderObjects#_updateCompositingBits
Flush Paint
1 | void flushPaint() { |
对于 Paint 操作来说,父节点需要用到子节点绘制的结果,故子节点需要先于父节点被绘制。
因此,不同于前两个 flush 操作,此时需要对『 Needing Paint RenderObjects 』按深度降序排序。
如下图,在深入浅出 Flutter Framework 之 PaintingContext一文中详细分析了从PipelineOwner#flushPaint到PaintingContext内部操作的过程,在此不再赘述。
Flush Semantics
1 | void flushSemantics() { |
Flush Semantics 所做操作与 Flush Layout 完全相似,不再赘述。
至此,PipelineOwner 相关的内容就介绍完了。
小结
PipelineOwner 作为『 RenderObject Tree』与『 RendererBinding/Window』间的沟通协调桥梁,在整个 Rendering Pipeline 中起到重要作用。
在 Flutter 应用生命周期内,不断收集『 Dirty RenderObjects 』并及时通知 Engine。
在帧刷新时,通过来自 RendererBinding 的回调依次处理收集到的:
- 『 Needing Layout RenderObjects 』
- 『 Needing Compositing Bits Update RenderObjects 』
- 『 Needing Paint RenderObjects 』
- 『 Needing Semantics RenderObjects 』
最终完成 UI 的刷新。