Flutter在线编程实践总结
1.Flutter架构
Flutter的架构主要分成三层:Framework,Engine,Embedder。
1.Framework使⽤dart实现,包括Material Design风格的Widget,Cupertino(针对iOS)风格的Widgets,⽂本/图⽚/按钮等基础Widgets,渲染,动画,⼿势等。 此部分的核⼼代码是:flutter仓库下的flutter package,以及sky_engine仓库下的io,async,ui(dart:ui库提供了Flutter框架和引擎之间的接⼝)等package。
2.Engine使⽤C++实现,主要包括:Skia,Dart和Text。Skia是开源的⼆维图形库,提供了适⽤于多种软硬件平台的通⽤API。
3.Embedder是⼀个嵌⼊层,即把Flutter嵌⼊到各个平台上去,这⾥做的主要⼯作包括渲染Surface设置,线程设置,以及插件等。 从这⾥可以看出,Flutter的平台相关层很低,平台(如iOS)只是提供⼀个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好的跨端⼀致性。
2.Flutter视图绘制
对于开发者来说,使⽤最多的还是framework,我就从Flutter的⼊⼝函数开始⼀步步往下⾛,分析⼀下Flutter视图绘制的原理。
在Flutter应⽤中,main()函数最简单的实现如下
// 参数app是⼀个widget,是Flutter应⽤启动后要展⽰的第⼀个Widget。
void runApp(Widget app) {
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
1.WidgetsFlutter Binding
WidgetsFlutterBinding继承⾃BindingBase 并混⼊了很多Binding,查看这些 Binding的源码可以发现这些Binding中基本都是监听并处理Window对象(包含了当前设备和系统的⼀些信息以及Flutter Engine的⼀些回调)的⼀些事件,然后将这些事件按照Framework的模型包装、抽象然后分发。
WidgetsFlutterBinding正是粘连Flutter engine与上层Framework的“胶⽔”。
1. GestureBinding:提供了PointerDataPacket 回调,绑定Framework⼿势⼦系统,是Framework事件模型与底层事件
的绑定⼊⼝。
2. ServicesBinding:提供了PlatformMessage 回调, ⽤于绑定平台消息通道(message channel),主要处理原⽣和
Flutter通信。
3. SchedulerBinding:提供了BeginFrame和DrawFrame回调,监听刷新事件,绑定Framework绘制调度⼦
系统。
4. PaintingBinding:绑定绘制库,主要⽤于处理图⽚缓存。
5. SemanticsBinding:语义化层与Flutter engine的桥梁,主要是辅助功能的底层⽀持。
6. RendererBinding: 提供了MetricsChanged 、TextScaleFactorChanged 等回调。它是渲染树与Flutter
engine的桥梁。
7. WidgetsBinding:提供了LocaleChanged、onBuildScheduled 等回调。它是Flutter widget层与engine的桥梁。
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBi nding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
看到这个WidgetsFlutterBinding混⼊(with)很多的Binding,下⾯先看⽗类BindingBase:
abstract class BindingBase {
...
ui.SingletonFlutterWindow get window => ui.window;//获取window实例
@protected
@mustCallSuper
void initInstances() {
assert(!_debugInitialized);
assert(() {
_debugInitialized = true;
return true;
}());
}
}
看到有句代码Window get window => ui.window链接宿主操作系统的接⼝,也就是Flutter framework 链接宿主操作系统的接⼝。系统中有⼀个Window实例,可以从window属性来获取,看看源码:
// window的类型是⼀个FlutterView,FlutterView⾥⾯有⼀个PlatformDispatcher属性
ui.SingletonFlutterWindow get window => ui.window;
// 初始化时把PlatformDispatcher.instance传⼊,完成初始化
ui.window = SingletonFlutterWindow._(0, PlatformDispatcher.instance);
// SingletonFlutterWindow的类结构
class SingletonFlutterWindow extends FlutterWindow {
...
// 实际上是给BeginFrame赋值
FrameCallback? get onBeginFrame => BeginFrame;
set onBeginFrame(FrameCallback? callback) {
}
VoidCallback? get onDrawFrame => DrawFrame;
set onDrawFrame(VoidCallback? callback) {
}
// window.scheduleFrame实际上是调⽤platformDispatcher.scheduleFrame()
void scheduleFrame() => platformDispatcher.scheduleFrame();
...
}
class FlutterWindow extends FlutterView {
FlutterWindow._(this._windowId, this.platformDispatcher);
final Object _windowId;
// PD
@override
final PlatformDispatcher platformDispatcher;
@override
ViewConfiguration get viewConfiguration {
return platformDispatcher._viewConfigurations[_windowId]!;
}
}
2.sc heduleAtta c hR o o tWidget
scheduleAttachRootWidget紧接着会调⽤WidgetsBinding的attachRootWidget⽅法,该⽅法负责将根Widget添加到RenderView 上,代码如下:
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
)
.attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance!.ensureVisualUpdate();
}
}
renderView变量是⼀个RenderObject,它是渲染树的根。renderViewElement变量是renderView对应的Element对象。可见该⽅法主要完成了根widget到根 RenderObject再到根Element的整个关联过程。
RenderView get renderView => _Node! as RenderView;
renderView是RendererBinding中拿到Node,PipelineOwner在 Rendering Pipeline 中起到重要作⽤:
随着 UI 的变化⽽不断收集『 Dirty Render Objects 』随之驱动 Rendering Pipeline 刷新 UI。
简简单讲,PipelineOwner是『RenderObject Tree』与『RendererBinding』间的桥梁。
最终调⽤attachRootWidget,执⾏会调⽤RenderObjectToWidgetAdapter的attachToRenderTree⽅法,该⽅法负责创建根element,即RenderObjectToWidgetElement,并且将element与widget 进⾏关联,即创建出 widget树对应的element树。如果element 已经创建过了,则将根element 中关联的widget 设为新的,由此可以看出element 只会创建⼀次,后⾯会进⾏复⽤。BuildOwner是widget framework的管理类,它跟踪哪些widget需要重新构建。代码如下
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
});
} else {flutter开发app
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
3.sc heduleWa rm U pFr a m e
r mU
runApp的实现中,当调⽤完attachRootWidget后,最后⼀⾏会调⽤ WidgetsFlutterBinding 实例的 scheduleWarmUpFrame() ⽅法,该⽅法的实现在SchedulerBinding 中,它被调⽤后会⽴即进⾏⼀次绘制(⽽不是等待"vsync" 信号),在此次绘制结束前,该⽅法会锁定事件分发,也就是说在本次绘制结束完成之前Flutter将不会响应各种事件,这可以保证在绘制过程中不会再触发新的重绘。
下⾯是scheduleWarmUpFrame() ⽅法的部分实现(省略了⽆关代码):
void scheduleWarmUpFrame() {
…
Timer.run(() {
handleBeginFrame(null);
});
Timer.run(() {
handleDrawFrame();
resetEpoch();
});
// 锁定事件
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
…
}
该⽅法中主要调⽤了handleBeginFrame() 和 handleDrawFrame() 两个⽅法
查看handleBeginFrame() 和 handleDrawFrame() 两个⽅法的源码,可以发现前者主要是执⾏了transientCallbacks队列,⽽后者执⾏了 persistentCallbacks 和 postFrameCallbacks 队列。
1. transientCallbacks:⽤于存放⼀些临时回调,⼀般存放动画回调。
可以通过SchedulerBinding.instance.scheduleFrameCallback 添加回调。
2. persistentCallbacks:⽤于存放⼀些持久的回调,不能在此类回调中再请求新的绘制帧,持久回调⼀经注册则不能移除。
SchedulerBinding.instance.addPersitentFrameCallback(),这个回调中处理了布局与绘制⼯作。
3. postFrameCallbacks:在Frame结束时只会被调⽤⼀次,调⽤后会被系统移除,可由 SchedulerBinding.instance.addPostFrameCallback() 注册。
注意,不要在此类回调中再触发新的Frame,这可以会导致循环
真正的渲染和绘制逻辑在RendererBinding中实现,查看其源码,发现在其initInstances()⽅法中有如下代码:
void initInstances() {
... // 省略⽆关代码
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout(); // 布局
pipelineOwner.flushCompositingBits(); //重绘之前的预处理操作,检查RenderObject是否需要重绘
pipelineOwner.flushPaint(); // 重绘
renderViewpositeFrame(); // 将需要绘制的⽐特数据发给GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}
需要注意的是:由于RendererBinding只是⼀个mixin,⽽with它的是WidgetsBinding,所以需要看看WidgetsBinding中是否重写该⽅法,查看WidgetsBinding的drawFrame()⽅法源码:
@override
void drawFrame() {
...//省略⽆关代码
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame(); //调⽤RendererBinding的drawFrame()⽅法
buildOwner.finalizeTree();
}
}
在调⽤RendererBinding.drawFrame()⽅法前会调⽤ buildOwner.buildScope() (⾮⾸次绘制),该⽅法会将被标记为“dirty” 的element 进⾏ rebuild()
我们再来看WidgetsBinding,在initInstances()⽅法中创建BuildOwner对象,然后执⾏buildOwner!.onBuildScheduled =
_handleBuildScheduled;,这⾥将_handleBuildScheduled赋值给了buildOwnder的onBuildScheduled属性。
BuildOwner对象,它负责跟踪哪些widgets需要重新构建,并处理应⽤于widgets树的其他任务,其内部维护了⼀个_dirtyElements列表,⽤以保存被标“脏”的elements。
每⼀个element被新建时,其BuildOwner就被确定了。⼀个页⾯只有⼀个buildOwner对象,负责管理该页⾯所有的element。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论