main()
函数中。在Flutter应用中,main()
函数最简单的实现如下: void main() {
runApp(MyApp());
}
main()
函数只调用了一个runApp()
方法,runApp()
方法中都做了什么: void runApp(Widget app) {
//初始化操作
WidgetsFlutterBinding.ensureInitialized()
//页面渲染
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
* 参数`app`是一个Widget,是Flutter应用启动后要展示的第一个Widget。
* `WidgetsFlutterBinding`正是绑定widget 框架和Flutter engine的桥梁。
ensureInitialized()
方法 class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
* 可以看到`WidgetsFlutterBinding`继承自`BindingBase` 并混入了很多`Binding`,在介绍`Binding`之前先介绍一下`Window`,`Window`的官方解释:The most basic interface to the host operating system's user interface.
Window
正是Flutter Framework连接宿主操作系统的接口。看一下Window
类的部分定义: class Window {
// 当前设备的DPI,即一个逻辑像素显示多少物理像素,数字越大,显示效果就越精细保真。
// DPI是设备屏幕的固件属性,如Nexus 6的屏幕DPI为3.5
double get devicePixelRatio => _devicePixelRatio;
// 绘制回调
VoidCallback get onDrawFrame => _onDrawFrame;
// 发送平台消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) ;
... //其它属性及回调
}
* `Window`类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在回来看看`WidgetsFlutterBinding`混入的各种Binding。通过查看这些 Binding的源码,可以发现这些Binding中基本都是监听并处理`Window`对象的一些事件,然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到`WidgetsFlutterBinding`正是粘连Flutter engine与上层Framework的“胶水”。
attachToRenderTree
的源码实现: RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
if (element == null) {
...// 代码处理
} else {
...// 代码处理
}
return element;
}
RenderObjectToWidgetElement
,并且将element与widget 进行关联,即创建出 widget树对应的element树。
BuildOwner
是什么呢?其实他就是widget framework的管理类,它跟踪哪些widget需要重新构建。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()` 两个方法,在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念:
* Frame: 一次绘制过程,我们称其为一帧。Flutter engine受显示器垂直同步信号"VSync"的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps(Frame Per-Second),就是指一秒钟可以触发60次重绘,FPS值越大,界面就越流畅。
* FrameCallback:`SchedulerBinding` 类中有三个FrameCallback回调队列, 在一次绘制过程中,这三个回调队列会放在不同时机被执行:
* 1. `transientCallbacks`:用于存放一些临时回调,一般存放动画回调。可以通过`SchedulerBinding.instance.scheduleFrameCallback` 添加回调。
* 2. `persistentCallbacks`:用于存放一些持久的回调,不能在此类回调中再请求新的绘制帧,持久回调一经注册则不能移除。`SchedulerBinding.instance.addPersitentFrameCallback()`,这个回调中处理了布局与绘制工作。
* 3. `postFrameCallbacks`:在Frame结束时只会被调用一次,调用后会被系统移除,可由 `SchedulerBinding.instance.addPostFrameCallback()` 注册,注意,不要在此类回调中再触发新的Frame,这可以会导致循环刷新。
* 自行查看`handleBeginFrame()` 和 `handleDrawFrame()` 两个方法的源码,可以发现前者主要是执行了`transientCallbacks`队列,而后者执行了 `persistentCallbacks` 和 `postFrameCallbacks` 队列。
RendererBinding
中实现,查看其源码,发现在其initInstances()
方法中有如下代码: void initInstances() {
... //省略无关代码
//监听Window对象的事件
ui.window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
//添加PersistentFrameCallback
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
* 看最后一行,通过`addPersistentFrameCallback` 向`persistentCallbacks`队列添加了一个回调 `_handlePersistentFrameCallback`:
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
RendererBinding
的drawFrame()
方法 void flushLayout() {
...
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = <RenderObject>[];
for (RenderObject node in
dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize();
}
}
}
}
RenderObject
的布局信息。主要的动作发生在node._layoutWithoutResize()
方法中,该方法中会调用performLayout()
进行重新布局。 void flushCompositingBits() {
_nodesNeedingCompositingBitsUpdate.sort(
(RenderObject a, RenderObject b) => a.depth - b.depth
);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits(); //更新RenderObject.needsCompositing属性值
}
_nodesNeedingCompositingBitsUpdate.clear();
}
RenderObject
是否需要重绘,然后更新RenderObject.needsCompositing
属性,如果该属性值被标记为true
则需要重绘。 void flushPaint() {
...
try {
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
// 反向遍历需要重绘的RenderObject
for (RenderObject node in
dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
// 真正的绘制逻辑
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
}
}
* 该方法进行了最终的绘制,可以看出它不是重绘了所有 `RenderObject`,而是只重绘了需要重绘的 `RenderObject`。真正的绘制是通过`PaintingContext.repaintCompositedChild()`来绘制的,该方法最终会调用Flutter engine提供的Canvas API来完成绘制。
void compositeFrame() {
...
try {
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
ui.window.render(scene); //调用Flutter engine的渲染API
scene.dispose();
} finally {
Timeline.finishSync();
}
}
Scene
对象,Scene对象是一个数据结构,保存最终渲染后的像素信息。
Scene
传给window.render()
方法,该方法会直接将scene信息发送给Flutter engine,最终由engine将图像画在设备屏幕上。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()
。如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓