从源码看flutter系列集合canvas
这一篇,咱们将简单的了解一下 Layer
相关内容,由于其中大部分是与C++交互,因此只是从结构上作一个认识与分析bash
从上一篇中,咱们了解到了,若是 RenderObject
的 isRepaintBoundary
为 true 会经过本身的 Layer
对象去渲染,若是没有为 RenderObject
手动指定 Layer
的话,默认会是 OffestLayer
;为 false 则经过父节点的 Layer
对象渲染。app
其中 paint 相关的 Layer
逻辑都在 PaintingContext
中,每次 paint 都会建立一个新的 PaintingContext
对象dom
同时经过 PaintingContext
获取 Canvans
时会建立一个 PictureLayer
被合成到 PaintingContext
的建立时所接收的 Layer
中ide
下面,咱们简单的看一下 Layer
对象布局
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
@override
ContainerLayer get parent => super.parent as ContainerLayer;
...
Layer get nextSibling => _nextSibling;
...
Layer get previousSibling => _previousSibling;
...
@protected
void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);
...
}
复制代码
Layer
和 RenderObject
同样,都是 AbstractNode
的子类,能够看到持有的 parent
对象都是 ContainerLayer
,同时 Layer
还有两个对象 nextSibling
和 previousSibling
,看起来像一个双向链表的结构post
addToScene(...)
交由子类实现,就是将 Layer
对象交给 engine 去处理,传递给 engine 的逻辑都在 SceneBuilder
中测试
Layer
有多个子类,分别实现不一样的渲染功能ui
其中 PictureLayout
是主要的图像绘制层;
TextureLayer
则用于外界纹理的实现,经过它能够实现诸如相机、视频播放、OpenGL等相关操做;
ContainerLayout
则是各个 Layer
组成的复合层this
上一篇中,咱们自定义了 RenderObject
而且重写了它的 paint(...)
方法,经过对 Canvans
对象进行操做,咱们绘制了本身想要的图像,而 Canvans
是从 PaintingContext
中获取的,在获取 Canvans
时,其实作了和 Layer
有关的一系列操做
class PaintingContext extends ClipContext {
...
@override
Canvas get canvas {
if (_canvas == null)
_startRecording();
return _canvas;
}
void _startRecording() {
assert(!_isRecording);
_currentLayer = PictureLayer(estimatedBounds);
_recorder = ui.PictureRecorder();
_canvas = Canvas(_recorder);
_containerLayer.append(_currentLayer);
}
...
}
复制代码
能够看到,在这里建立了一个新的 PictureLayer
被添加到了 _containerLayer
中,咱们的 Layer
最终是如何被渲染的呢?
信息仍是能够从上一篇得到,咱们知道 RendererBinding
的 drawFrame()
中进行了布局与绘制操做
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
复制代码
最终的渲染实际上是经过 compositeFrame()
来进行的,而这里的 renderView
就是咱们的根RenderObject
咱们能够看一下 compositeFrame()
作了些什么
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
...
void compositeFrame() {
...
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
_window.render(scene);
scene.dispose();
...
}
...
}
复制代码
能够看到,这里经过 buildScene(...)
建立了 Scene
对象,根据注释来看,它至关于一颗 Layer
树
以后经过 Window
对象的 render(...)
方法来进行渲染
void render(Scene scene) native 'Window_render';
复制代码
它直接调用的是 engine 的方法,经过这个方法,就能够对图像进行渲染,后面咱们会进行一个测试来展现它的做用
这里的 layer
就是根 Layer
,它实际上是一个 TransformLayer
,咱们能够简单看一下它的建立流程
在 RendererBinding
的 initInstances()
中经过 initRenderView()
进行了 RenderView
的建立
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
...
@override
void initInstances() {
super.initInstances();
...
initRenderView();
...
}
...
}
复制代码
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
复制代码
根 Layer
就是在 prepareInitialFrame()
中建立的
void prepareInitialFrame() {
...
scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
...
}
复制代码
建立的方法就是 _updateMatricesAndCreateNewRootLayer()
TransformLayer _updateMatricesAndCreateNewRootLayer() {
_rootTransform = configuration.toMatrix();
final TransformLayer rootLayer = TransformLayer(transform: _rootTransform);
rootLayer.attach(this);
...
return rootLayer;
}
复制代码
这里咱们只是简单的了解一下根 Layer
的建立流程,它就是一个 TransformLayer
对象。
建立流程咱们知道了,而想要了解刷新流程,咱们须要回到 compositeFrame()
方法中,执行刷新的方法就在 buildScene(...)
里
从前面的关系图咱们知道,TransformLayer
的父类是 OffsetLayer
,而 OffsetLayer
的父类是 ContainerLayer
,它们都没有重写 buildScene(...)
方法,因此最后会调用 ContainerLayer
的 buildScene(...)
ui.Scene buildScene(ui.SceneBuilder builder) {
...
updateSubtreeNeedsAddToScene();
addToScene(builder);
...
_needsAddToScene = false;
...
return scene;
}
复制代码
能够先看一下 updateSubtreeNeedsAddToScene()
方法
///ConstraintLayer
@override
void updateSubtreeNeedsAddToScene() {
super.updateSubtreeNeedsAddToScene();
Layer child = firstChild;
while (child != null) {
child.updateSubtreeNeedsAddToScene();
_needsAddToScene = _needsAddToScene || child._needsAddToScene;
child = child.nextSibling;
}
}
///Layer
@protected
@visibleForTesting
void updateSubtreeNeedsAddToScene() {
_needsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
}
复制代码
其实全部 Layer
类中,只有 ConstraintLayer
和 Layer
具有这两个方法,这里其实就是遍历全部子 Layer
对象,调用他们的 updateSubtreeNeedsAddToScene()
来设置 _needsAddToScene
的值
这个值顾名思义,就是表示是否须要将改 Layer
添加到 Scene
中,若是须要添加,则就是进行刷新了。它根据 _needsAddToScene
和 alwaysNeedsAddToScene
来设置,当调用 markNeedsAddToScene()
方法的时候, _needsAddToScene
就会被设置为 true
updateSubtreeNeedsAddToScene()
执行结束后,接下来会调用 addToScene(builder)
方法
正好 TransformLayer
重写了这个方法,而且没有调用父类的方法
@override
void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
...
engineLayer = builder.pushTransform(
_lastEffectiveTransform.storage,
oldLayer: _engineLayer as ui.TransformEngineLayer,
);
addChildrenToScene(builder);
builder.pop();
}
复制代码
这里的 engineLayer
对象是用于进行复用的
能够看到这里调用了 addChildrenToScene(builder)
方法,这个方法只在 ContainerLayer
中,且没有被重写
void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) {
Layer child = firstChild;
while (child != null) {
if (childOffset == Offset.zero) {
child._addToSceneWithRetainedRendering(builder);
} else {
child.addToScene(builder, childOffset);
}
child = child.nextSibling;
}
}
复制代码
在这里就是遍历 child ,而后调用它们各自实现的 addToScene(...)
方法,而是否要将 Layer
添加到 Scene
的判断依据,已经在以前的 updateSubtreeNeedsAddToScene()
中完成了。
这里须要注意一下, _addToSceneWithRetainedRendering(builder)
就是用于对以前的 _engineLayer
进行复用,当 childOffset
为 Offset.zero
时
那么到这里 Layer
的刷新流程就结束了。而本篇文章差很少也快到头了,接下来咱们完成上面提到过的,进行一个渲染测试
能够在这里进行测试:dartpad.dev/
import 'dart:ui';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main(){
final OffsetLayer rootLayer = new OffsetLayer();
final PictureLayer pictureLayer = new PictureLayer(Rect.zero);
rootLayer.append(pictureLayer);
PictureRecorder recorder = PictureRecorder();
Canvas canvas = Canvas(recorder);
Paint paint = Paint();
paint.color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
canvas.drawRect(Rect.fromLTWH(0, 0, 300, 300), paint);
pictureLayer.picture = recorder.endRecording();
SceneBuilder sceneBuilder = SceneBuilder();
rootLayer.addToScene(sceneBuilder);
Scene scene = sceneBuilder.build();
window.onDrawFrame = (){
window.render(scene);
};
window.scheduleFrame();
}
复制代码
效果以下
能够看到,咱们没有使用任何 Widget
就在设备上展现了一个图案。因此其实从这里就能够了解到为何说 Flutter是经过skia引擎去绘制的了。
关于 Layer
的其余内容,这里也再也不深刻了,毕竟再深刻就是C++了
本篇是咱们讲过的四棵树中最后的一颗,而这里很是方便用于测试一个咱们前三篇都遇到了可是都略过了的部分,那就是 热重载
当你经过上面的用例进行测试的时候,点击一下热重载按钮,是否是发现会报错:
Error -32601 received from application: Method not found
复制代码
而且图案的颜色并不会更改,这就涉及到咱们以前提到过的一个方法了:reassemble()
了
在 Element
和 RenderObject
中你常常能看到与之相关的方法,它就是用于实现热重载的核心逻辑
在 BindingBase
中,咱们能够看到找到这样一个方法:reassembleApplication()
,就是它来进行热重载控制的
它会调用 performReassemble()
方法
@mustCallSuper
@protected
Future<void> performReassemble() {
FlutterError.resetErrorCount();
return Future<void>.value();
}
复制代码
在 WidgetsBinding
和 RendererBinding
都重写了这个方法,若是感兴趣的话,能够去看一下,他们分在其中调用了让 Element
和 RenderObject
进行热重载的方法
那么,咱们想要实现实现热重载其实就很简单了,看代码:
import 'dart:ui';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/foundation.dart';
void main() => TestBinding();
class TestBinding extends BindingBase{
@override
Future<void> performReassemble(){
final OffsetLayer rootLayer = new OffsetLayer();
final PictureLayer pictureLayer = new PictureLayer(Rect.zero);
rootLayer.append(pictureLayer);
PictureRecorder recorder = PictureRecorder();
Canvas canvas = Canvas(recorder);
Paint paint = Paint();
paint.color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
canvas.drawRect(Rect.fromLTWH(0, 0, 300, 300), paint);
pictureLayer.picture = recorder.endRecording();
SceneBuilder sceneBuilder = SceneBuilder();
rootLayer.addToScene(sceneBuilder);
Scene scene = sceneBuilder.build();
window.onDrawFrame = (){
window.render(scene);
};
window.scheduleFrame();
super.performReassemble();
return Future<void>.value();
}
}
复制代码
热重载效果以下,你们能够在设备上进行测试
固然,热重载的核心逻辑就是这个了。
不过此前会进行代码文件的变动检查等,详情能够看这一篇文章:揭秘Flutter Hot Reload(原理篇)
本篇到这里就结束了,而【从源码看flutter】 还没有结束,敬请期待吧