Flutter框架分析(二)-- 初始化


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设置了onBeginFrameonDrawFrame的回调,回忆一下上一篇文章讲渲染流水线的时候,当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.navigationSystemChannels.system的消息处理函数。这两个回调一个是专门处理路由的,另一个是处理一些系统事件,比如剪贴板,震动反馈,系统音效等等。

至此,WidgetsFlutterBinding.ensureInitialized()就跑完了,总体上来讲是把window提供的API分别封装到不同的Binding里。我们需要重点关注的是SchedulerBindingRendererBindingWidgetsBinding。这3个是渲染流水线的重要存在。

接下来就该看一下runApp()里的第二个调用了。

attachRootWidget(app)

这个函数的代码如下:

void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: ‘[root]’,
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}

在之前说的RendererBinding的初始化的时候,我们得到了一个RenderView的实例,render tree的根节点。RenderView是继承自RenderObject的,而RenderObject需要有对应的WidgetElement。上述代码中的RenderObjectToWidgetAdapter就是这个Widget。而对应的Element就是RenderObjectToWidgetElement了,既然是要关联到render tree的根节点,那它自然也就是element tree的根节点了。

从上述分析我们可以得出结论:

  • 渲染绑定(RendererBinding)通过pipelineOwner间接持有render tree的根节点RenderView
  • 组件绑定(WidgetsBinding)持有element tree的根节点RenderObjectToWidgetElement

那么RenderObjectToWidgetElement是怎么和RenderView关联起来的呢,那自然是通过一个Widget做到的了,看下RenderObjectToWidgetAdapter的代码:

class RenderObjectToWidgetAdapter 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 createElement() => RenderObjectToWidgetElement(this);

@override
RenderObjectWithChildMixin 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);

});

总结

首先是感觉自己的基础还是不够吧,大厂好像都喜欢问这些底层原理。

另外一部分原因在于资料也还没有看完,一面时凭借那份资料考前突击恶补个几天居然也能轻松应对(在这里还是要感谢那份资料,真的牛),于是自我感觉良好,资料就没有怎么深究下去了。

之前的准备只涉及了Java、Android、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。

下一步还是要查漏补缺,进行针对性复习。

最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!希望大家不要犯和我一样的错误呀!!!一定要看完!


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
片转存中…(img-B2YXxehc-1714879671201)]

[外链图片转存中…(img-InuZFoW7-1714879671203)]

[外链图片转存中…(img-NUTL8rph-1714879671205)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值