Flutter RendererBinding作用源码分析
WeninerIo 人气:0分析
RendererBinding 的作用是负责render tree 和flutter engine之间的连接. 我们在启动App的时候,首先会创建 PiplineOwner ,然后通过platformDispatcher去监听屏幕分辨率变化、系统文字大小变化、亮度、语义等等.最后去初始化RenderView,根据平台去处理如帧回调、鼠标、web之类的信息.
void initInstances() { super.initInstances(); _instance = this; _pipelineOwner = PipelineOwner( onNeedVisualUpdate: ensureVisualUpdate, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, ); platformDispatcher ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; initRenderView(); _handleSemanticsEnabledChanged(); assert(renderView != null); addPersistentFrameCallback(_handlePersistentFrameCallback); initMouseTracker(); if (kIsWeb) { addPostFrameCallback(_handleWebFirstFrame); } }
PipelineOwner
这里我们着重讲一下PipelineOwner, 官方描述中有这么一句话The pipeline owner manages the rendering pipeline., 也就是说 PipelineOwner帮我们管理着渲染所需要的. 我们根据PipelineOwner调用的顺序依次讲解下它提供的方法.
flushLayout
这个阶段将计算每个渲染对象的大小和位置, 渲染对象可能会在绘制或者compositing state 的时候被标成dirty,这是什么意思呢? 让我们回归到代码中
// 持有需要被布局的对象 List<RenderObject> _nodesNeedingLayout = <RenderObject>[]; { 非release包运行代码忽略 ... try { // 如果当前的节点需要合成 while (_nodesNeedingLayout.isNotEmpty) { final List<RenderObject> dirtyNodes = _nodesNeedingLayout; _nodesNeedingLayout = <RenderObject>[]; for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) { if (node._needsLayout && node.owner == this) node._layoutWithoutResize(); } } } finally { ... } }
代码中可以看到, 如果 _nodesNeedingLayout 中对象不为空.说明当前需要被布局,计算其大小.我们可以看到在依次处理节点时,最后一步是执行 _layoutWithoutResize() ,这个方法调用的本质实际上也就是 performLayout(). 那么, performLayout() 做了什么呢? 如果对自定义布局有过了解, 通常我们在实现 performLayout() 的时候.会先去 layout widget . 然后去通过position将widget 定位. 确定好widget在父widget中的相对位置.
flushCompositingBits
这个阶段中, 每个渲染对象都会了解其子对象是否需要合成.在绘制的阶段选择如何实现视觉效果. 这里实际上也就是标记所有的子对象是否需要合成
void flushCompositingBits() { ... _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth); for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) { if (node._needsCompositingBitsUpdate && node.owner == this) node._updateCompositingBits(); } _nodesNeedingCompositingBitsUpdate.clear(); ... }
flushPaint
在这个阶段,我们将会真正的绘制出Layer
void flushPaint() { ... try { final List<RenderObject> dirtyNodes = _nodesNeedingPaint; _nodesNeedingPaint = <RenderObject>[]; // Sort the dirty nodes in reverse order (deepest first). for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) { ... if (node._needsPaint && node.owner == this) { if (node._layerHandle.layer!.attached) { PaintingContext.repaintCompositedChild(node); } else { node._skippedPaintingOnLayer(); } } } ... } finally { ... } }
在绘制的时候将会判断当前的layer是否attached,如果不是attched的状态.则说明当前的layer已经调用detach方法,因此不再需要绘制.所以会跳过绘制,执行 _skippedPaintingOnLayer() 的方法. 如果是attached的状态,则需要调用 repaintCompositedChild() 的方法
flushSemantics
最后,如果启用了语义. 这个阶段将会编译渲染对象的语义,这里就不过多介绍了.
initRenderView
如果说还有比较重要的方法需要讲解, 那么就是 initRenderView() 这个方法了.这里将会创建一个 RenderView的对象作为RenderObject的根 ,同时对它进行初始化.
void initRenderView() { renderView = RenderView(configuration: createViewConfiguration(), window: window); // 准备第一帧启动渲染通道. 这里只会调用一次. renderView.prepareInitialFrame(); }
在 PrepareInitalFrame() 中, 我们通过 scheduleInitialLayout和scheduleInitialPaint , 安排微事务队列尽可能快的处理layout和paint.
scheduleInitialLayout
在这个阶段,主要是将owner的_nodesNeedingLayout 对象中加入这个初始化的renderview.
scheduleInitialPaint
这个阶段中, 我们将_layerHandle 中的layer 赋值成当前layer.并在owner中加入 _nodesNeedingPaint .
void scheduleInitialPaint(ContainerLayer rootLayer) { _layerHandle.layer = rootLayer; owner!._nodesNeedingPaint.add(this); }
今天的RendererBinding源码分析就暂告一个段落了,它主要是负责了测量布局、绘制之类的方法. 作为一个入口还是有了解的必要的, 建议大家有时间可以多看看.
加载全部内容