…
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(
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 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学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!