内容简介:上篇文章那么我们就从函数
上篇文章 《Flutter框架分析(一)-- 总览和Window》 介绍了Flutter框架最核心的渲染流水线和最基础的 Window 。这篇文章里,我们从Flutter框架的初始化来进入,来一步步揭开Flutter的面纱。写过Flutter程序的同学都知道,Flutter app的入口就是函数 runApp() 。
void main() {
runApp(MyApp());
}
复制代码
那么我们就从函数 runApp() 入手,看看这个函数被调用以后发生了什么。
初始化
runApp() 的函数体位于 widgets/binding.dart 。长这样:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
复制代码
从调用的函数名称就可以看出来,它做了3件事,
WidgetsFlutterBinding
OK,那我们就来挨个看一下这3件事具体都做了什么。
ensureInitialized()
首先我们先看一下 WidgetsFlutterBinding 是什么,从这个类的名称来看,是把Widget和Flutter绑定在一起的意思。
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
复制代码
这个类继承自 BindingBase 并且混入( Mixin )了很多其他类,看名称都是不同功能的绑定。而静态函数 ensureInitialized() 所做的就是返回一个 WidgetsBinding.instance 单例。
混入的那些各种绑定类也都是继承自抽象类 BindingBase 的。
abstract class BindingBase {
BindingBase() {
...
initInstances();
...
}
...
ui.Window get window => ui.window;
}
复制代码
关于抽象类 BindingBase ,你需要了解两个地方,一个是在其构造的时候会调用函数 initInstances() 。这个函数会由其子类,也就是上面说那些各种混入(Mixin)的绑定类各自实现,具体的初始化都是在其内部实现的。另一个就是 BindingBase 有一个 getter ,返回的是 window 。还记得在 《Flutter框架分析(一)-- 总览和Window》 中提到过的窗口吗?没错,这里的 window 就是它。那我们是不是可以推论,这些个绑定其实就是对 window 的封装?来,让我们挨个看一下这几个绑定类在调用 initInstances() 的时候做了什么的吧。
第一个是 GestureBinding 。手势绑定。
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;
}
复制代码
在调用 initInstances() 的时候,主要做的事情就是给 window 设置了一个手势处理的回调函数。所以这个绑定主要是负责管理手势事件的。
第二个是 ServicesBinding 。服务绑定
mixin ServicesBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
window
..onPlatformMessage = BinaryMessages.handlePlatformMessage;
initLicenses();
}
复制代码
这个绑定主要是给 window 设置了处理Platform Message的回调。
第三个是 SchedulerBinding 。调度绑定。
mixin SchedulerBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
}
复制代码
这个绑定主要是给 window 设置了 onBeginFrame 和 onDrawFrame 的回调,回忆一下上一篇文章讲渲染流水线的时候,当Vsync信号到来的时候engine会回调Flutter的来启动渲染流程,这两个回调就是在 SchedulerBinding 管理的。
第四个是 PaintingBinding 。绘制绑定。
mixin PaintingBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();
}
复制代码
这个绑定只是创建了个图片缓存,就不细说了。
第五个是 SemanticsBinding 。辅助功能绑定。
mixin SemanticsBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = window.accessibilityFeatures;
}
复制代码
这个绑定管理辅助功能,就不细说了。
第六个是 RendererBinding 。渲染绑定。这是比较重要的一个类。
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
_mouseTracker = _createMouseTracker();
}
复制代码
这个绑定是负责管理渲染流程的,初始化的时候做的事情也比较多。 首先是实例化了一个 PipelineOwner 类。这个类负责管理驱动我们之前说的渲染流水线。随后给 window 设置了一系列回调函数,处理屏幕尺寸变化,亮度变化等。接着调用 initRenderView() 。
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame();
}
复制代码
这个函数实例化了一个 RenderView 类。 RenderView 继承自 RenderObject 。我们都知道Flutter框架中存在这一个渲染树(render tree)。这个 RenderView 就是渲染树(render tree)的根节点,这一点可以通过打开"Flutter Inspector"看到,在"Render Tree"这个Tab下,最根部的红框里就是这个 RenderView 。
最后调用 addPersistentFrameCallback 添加了一个回调函数。请大家记住这个回调,渲染流水线的主要阶段都会在这个回调里启动。
第七个是 WidgetsBinding ,组件绑定。
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
}
复制代码
这个绑定的初始化先给 buildOwner 设置了个 onBuildScheduled 回调,还记得渲染绑定里初始化的时候实例化了一个 PipelineOwner 吗?这个 BuildOwner 是在组件绑定里实例化的。它主要负责管理Widget的重建,记住这两个"owner"。他们将会Flutter框架里的核心类。接着给 window 设置了两个回调,因为和渲染关系不大,就不细说了。最后设置 SystemChannels.navigation 和 SystemChannels.system 的消息处理函数。这两个回调一个是专门处理路由的,另一个是处理一些系统事件,比如剪贴板,震动反馈,系统音效等等。
至此, WidgetsFlutterBinding.ensureInitialized() 就跑完了,总体上来讲是把 window 提供的API分别封装到不同的Binding里。我们需要重点关注的是 SchedulerBinding , RendererBinding 和 WidgetsBinding 。这3个是渲染流水线的重要存在。
接下来就该看一下 runApp() 里的第二个调用了。
attachRootWidget(app)
这个函数的代码如下:
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
复制代码
在之前说的 RendererBinding 的初始化的时候,我们得到了一个 RenderView 的实例,render tree的根节点。 RenderView 是继承自 RenderObject 的,而 RenderObject 需要有对应的 Widget 和 Element 。上述代码中的 RenderObjectToWidgetAdapter 就是这个 Widget 。而对应的 Element 就是 RenderObjectToWidgetElement 了,既然是要关联到render tree的根节点,那它自然也就是element tree的根节点了。
从上述分析我们可以得出结论:
- 渲染绑定(
RendererBinding)通过pipelineOwner间接持有render tree的根节点RenderView。 - 组件绑定(
WidgetsBinding)持有element tree的根节点RenderObjectToWidgetElement。
那么 RenderObjectToWidgetElement 是怎么和 RenderView 关联起来的呢,那自然是通过一个Widget做到的了,看下 RenderObjectToWidgetAdapter 的代码:
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
/// Creates a bridge from a [RenderObject] to an [Element] tree.
///
/// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription
}) : super(key: GlobalObjectKey(container));
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
...
}
复制代码
你看, createElement() 返回的就是 RenderObjectToWidgetElement ,而 createRenderObject 返回的 container 就是构造这个Widget传入的 RenderView 了。而我们自己的 MyApp 作为一个子widget存在于 RenderObjectToWidgetAdapter 之中。
最后调用的 attachToRenderTree 做的事情属于我们之前说的渲染流水线的构建(Build)阶段,这时会根据我们自己的widget生成element tree和render tree。构建(Build)阶段完成以后,那自然是要进入布局(Layout)阶段和绘制(Paint)阶段了。怎么进呢?那就是 runApp 里的最后一个函数调用了。
scheduleWarmUpFrame()
void scheduleWarmUpFrame() {
...
Timer.run(() {
...
handleBeginFrame(null);
...
});
Timer.run(() {
...
handleDrawFrame();
...
});
}
复制代码
这个函数其实就调了两个函数,就是之前我们讲 window 的时候说的两个回调函数 onBeginFrame 和 onDrawFrame 吗?这里其实就是在具体执行这两个回调。最后渲染出来首帧场景送入engine显示到屏幕。这里使用 Timer.run() 来异步运行两个回调,是为了在它们被调用之前有机会处理完微任务队列(microtask queue)。关于Dart代码异步执行可以参考我的文章 《Flutter/Dart中的异步》
我们之前说渲染流水线是由Vsync信号驱动的,但是上述过程都是在 runApp() 里完成的。并没有看到什么地方告诉engine去调度一帧。这是因为我们是在做Flutter的初始化。为了节省等待Vsync信号的时间,所以就直接把渲染流程跑完做出来第一帧图像来了。
总结
Flutter框架的初始化就介绍完了。顺带还包括了Flutter app首帧渲染的一个大致流程。本文中所说的Flutter框架初始化过程其实主要的点都在几个绑定(binding)的初始化。理解的时候要记住上篇文章中介绍的渲染流水线和 window 。Flutter框架其实就是围绕这两个东西在做文章。总结起来本文的要点这么几个:
- 3个重要绑定:
SchedulerBinding,RendererBinding和WidgetsBinding。 - 2个“owner”:
PipelineOwner和BuildOwner。 - 2颗树的根节点:render tree根节点
RenderView;element tree根节点RenderObjectToWidgetElement。
有了这些基础以后,后续的文章我们会再去分析 Widget , Element 和 RenderObject 之间的关系,以及具体的Flutter渲染流水线各阶段是如何工作的。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- hapi框架搭建记录(一):初始化项目
- Net开源框架ABP初探(一)— 项目初始化
- 浏览器攻击框架BeEF Part 2:初始化控制
- C++ 的一大误区——深入解释直接初始化与复制初始化的区别
- 初始化监听端口
- 类初始化导致死锁
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Approximation Algorithms
Vijay V. Vazirani / Springer / 2001-07-02 / USD 54.95
'This book covers the dominant theoretical approaches to the approximate solution of hard combinatorial optimization and enumeration problems. It contains elegant combinatorial theory, useful and inte......一起来看看 《Approximation Algorithms》 这本书的介绍吧!