什么是 Flutter 的 Binding
本文最后更新于 2021年5月13日 晚上
利用 Window 作为接口, 通过 Binding 来建立 Engine 和 Framework 的联系.
Binding 的初始化过程实际就是将 Framework 端许多函数或方法挂接到 Window 上的过程. 绑定过程的结果是生成一个 WidgetsBinding
单例对象, 可通过 WidgetsBinding.instance
访问. 更极端地讲: 只有通过 Binding, Framework 和 Engine 才能相互通信(不过有一个特例是 RenderView).
在 Flutter 中有如下 Binding, 首先是比较重要的四个:
- SchedulerBinding
- GestureBinding
- RendererBinding
- WidgetsBinding
其他几个:
- ServicesBinding: 主要负责处理平台通道中的消息传递.
- PaintingBinding: 主要负责处理 image 缓存.
- SemanticsBinding: 主要负责处理未来 Flutter 版本中的语义化相关内容.
- TestWidgetsFlutterBinding: 用于测试库中的 Widget tests.
下图是这些 Binding 和 window 之间的交互关系:
通过代码来看, WidgetsFlutterBinding
仅负责启动整个绑定过程, 所有绑定都在 ensureInitialized
类方法中完成, 即根据 mixin 的顺序执行每个 Binding mixin 中的 initInstances
方法:
1 |
|
而其中核心是 initInstances
串联起来的调用, 详见源码. (initInstances
里面的工作代码是从 GestureBinding
-> WidgetsBinding
这样的顺序进行的).
下面介绍四个比较重要的 binding, 具体的源码解析会放到单独的章节中, 这里不展开说, 仅从顶层进行分析.
SchedulerBinding 概述
这个绑定主要负责执行三大类的 callback:
- Transient callbacks: 由 window.onBeginFrame 驱动.
- Persistent callbacks: 由 window.onDrawFrame 驱动.
- Post-frame callbacks: 也是由 onDrawFrame 驱动, 但在
Persistent callbacks
执行之后.
另外它还负责执行一些非渲染类的工作, 这些工作一般是在两帧之间执行. 通过它内部的 schedulingStrategy
回调来决定这些工作的执行优先级. 默认值是 defaultSchedulingStrategy
方法.
它会监听应用的 lifecycle
事件(paused
/resumed
/inactive
)来打开或关闭 _framesEnabled
, 这个标志位控制能否向 window
发送 scheduleFrame()
消息:
resumed
/inactive
: 可以向window
发送scheduleFrame()
paused
: 不可以向window
发送scheduleFrame()
- 其他: 不改变当前状态
当它向 window 发送 scheduleFrame
消息后, window
会陆续调用它的 _handleBeginFrame
和 _handleDrawFrame
(挂接在 window 上的两个回调), 再通过它们来执行外部注册的三大类的 callback, 详见源码.
GestureBinding
这个绑定主要负责手势子系统, 包括光标事件的生命期和手势竞技场. 要明白 GestureBinding, 先要看 Flutter 中的手势系统, 有关手势系统的介绍, 会在 Flutter 手势系统一章中讲到.
RendererBinding
这个绑定的主要功能是作为 render tree 和 Flutter 引擎间的胶水.
WidgetsBinding
实际上帧渲染流程的核心实现是在 WidgetsBinding 中:
启动引擎-> main 函数 -> runApp -> RendererBinding 注册持久回调(addPersistentFrameCallback) -> _persistentCallbacks(在 window 收到 onDrawFrame
时被调用) -> RendererBinding 的那个持久回调会调用 drawFrame
, 在 WidgetsBinding 上面的 -> 整个布局渲染过程执行一次 -> 生成一帧.
最后一个问题: build 是如何调用的?
比如 StatelessWidget
的 build
方法, 调用顺序为: StatelessWidget 的 build <- StatelessElement 的 build <- ComponentElement 的 performRebuild <- Element 的 performRebuild <- Element 的 rebuild (dirty element) <- buildScope <- WidgetsBinding 的 drawFrame
, 而 drawFrame 又是由 onDrawFrame
中通过持久回调调用的, 这样就形成了一个完整流程. 当 build 执行完后, 实际就是更新了 Element 和 RenderObject 的配置, 然后通过这些信息来走一次渲染管线(在 RendererBinding
中的 drawFrame
), 将合成的数据送入 GPU, 就完成了一帧的生成和显示.
onDrawFrame
是由外部事件触发的 window 向引擎发送 scheduleFrame
, 再通过引擎反向调用 window 上的 onBeginFrame 和 onDrawFrame, 这样来触发的. 最开始的一帧是 main 函数主动向引擎发送消息生成的(scheduleWarmUpFrame
), 后续的帧都是由外部事件触发生成的, 如果是连续帧变化, 就是动画而已.
下一个需要关注的内容是 Widget 和 Element 和 Render 的相互配合生成界面的相关原理.
相关参考
- 官方文档.