我正在参与「启航方案」
main进口启动
- Flutter的主进口在”lib/main.dart”的
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; }
- 该办法担任创立根element,即
RenderObjectToWidgetElement
,并且将element与widget 进行相关,即创立出 widget树对应的element树。- 如果element 已经创立过了,则将根element 中相关的widget 设为新的,由此能够看出element 只会创立一次,后面会进行复用。那么
BuildOwner
是什么呢?其实他便是widget framework的管理类,它盯梢哪些widget需求从头构建。
- 如果element 已经创立过了,则将根element 中相关的widget 设为新的,由此能够看出element 只会创立一次,后面会进行复用。那么
页面烘托
- 回到
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回调行列, 在一次制作过程中,这三个回调行列会放在不同时机被执行:-
-
transientCallbacks
:用于寄存一些暂时回调,一般寄存动画回调。能够经过SchedulerBinding.instance.scheduleFrameCallback
增加回调。
-
-
-
persistentCallbacks
:用于寄存一些耐久的回调,不能在此类回调中再请求新的制作帧,耐久回调一经注册则不能移除。SchedulerBinding.instance.addPersitentFrameCallback()
,这个回调中处理了布局与制作工作。
-
-
-
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()
办法
flushLayout()
- 代码如下所示
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(); } } } }
- 源码很简略,该办法首要任务是更新了一切被标记为“dirty”的
RenderObject
的布局信息。首要的动作发生在node._layoutWithoutResize()
办法中,该办法中会调用performLayout()
进行从头布局。
flushCompositingBits()
- 代码如下所示
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
则需求重绘。
flushPaint()
- 代码如下所示
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来完结制作。
- 该办法进行了终究的制作,能够看出它不是重绘了一切
compositeFrame()
- 代码如下所示
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目标是一个数据结构,保存终究烘托后的像素信息。- 这个办法将Canvas画好的
Scene
传给window.render()
办法,该办法会直接将scene信息发送给Flutter engine,终究由engine将图像画在设备屏幕上。
- 这个办法将Canvas画好的
终究
- 需求留意:由于
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()
。