提示:本文设计到的 Flutter framework 层源码是基于 Flutter 1.20.0
void main() => runApp(MyApp());
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
三行代码代表了Flutter APP 启动的三个主流程:
- binding初始化(ensureInitialized)
- 绑定根节点(scheduleAttachRootWidget)
- 绘制热身帧(scheduleWarmUpFrame)
1 binding初始化(ensureInitialized)
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
WidgetsFlutterBinding的ensureInitialized()其实就是一个获取WidgetsFlutterBinding单例的过程,真正的初始化实现代码在其7个mixin中。7个mixin按照严格的先后调用链关系完成不同 binding 的初始化。
WidgetsFlutterBinding继承了BindingBase,在调用自己的构造器之前会先先执行了父类BindingBase构造函数。
BindingBase() {
developer.Timeline.startSync('Framework initialization');
assert(!_debugInitialized);
initInstances();
assert(_debugInitialized);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent('Flutter.FrameworkInitialization', {});
developer.Timeline.finishSync();
}
这里会调用initInstances() ,由于7个 mixin 都重写了initInstances(), with 的最后一个 WidgetsBinding 覆盖了前面的 binding 的 initInstances(),所以 WidgetsBinding 的 initInstances() 会首先被调用,而 WidgetsBinding 的 initInstances 函数中先通过 super 向上调用 initInstances ,所以 initInstances 的执行顺序依次是:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBinding -> WidgetsBinding,从而依次完成各个 Binding 的初始化相关工作。
1.1 GestureBinding
手势事件绑定,主要处理触屏幕指针事件的分发以及事件最终回调处理。
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;
}
这里将事件处理回调 _handlePointerDataPacket 赋值给 window,供window 收到屏幕指针事件后调用。window类是framework层与engine层处理屏幕相关事件的桥梁。_handlePointerDataPacket中会先调用hitTest进行命中测试。GestureBinding及RenderBinding都实现了hitTest方法,按照mixin顺序会优先调用RenderBinding.hitTest。RenderBinding.hitTest会从renderTree的根节点递归调用命中测试,返回命中的深度最大的节点到根节点路径上的所有节点。然后再执行dispatchEvent根据返回的hitTest命中节点列表遍历分发事件,事件分发的顺序是先子节点后父节点最终到根节点,类似Android的事件冒泡机制。
1.2 SchedulerBinding
绘制调度绑定
@override
void initInstances() {
super.initInstances();
_instance = this;
// debug编译模式时统计绘制流程时长,开始、运行、构建、光栅化。
if (!kReleaseMode) {
int frameNumber = 0;
addTimingsCallback((List timings) {
for (final FrameTiming frameTiming in timings) {
frameNumber += 1;
_profileFramePostEvent(frameNumber, frameTiming);
}
});
}
}
SchedulerBinding的作用就是在debug编译模式时统计绘制流程时长,开始、运行、构建、光栅化。
1.3 ServicesBinding
@override
void initInstances() {
super.initInstances();
_instance = this;
// 构建一个用于platform与flutter层通信的 BinaryMessenger
_defaultBinaryMessenger = createBinaryMessenger();
// 设置window监听回调,处理platform发送的消息
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLicenses();
// 设置处理platform发送的系统消息的 Handler
SystemChannels.system.setMessageHandler(handleSystemMessage);
// 设置AppLifecycleState生命周期回调
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
// AppLifecycleState 为 resumed 和 inactive 时才允许响应Vsync信号进行绘制
readInitialLifecycleStateFromNativeWindow();
}
ServicesBinding的初始化的工作主要是两个:
- platform与flutter层通信相关服务的初始化
- 注册监听了flutter app的生命周期变化事件,根据生命周期状态决定是否允许发起绘制任务
设置处理 system 消息 handleSystemMessage,ServicesBinding 的handleSystemMessage 主要工作是异步处理系统发送的内存紧张信号,PaintingBinding 也实现了该方法。由于 PaintingBinding 被 with 的顺序在 ServicesBinding 后面,所以 PaintingBinding 的 handleSystemMessage 会覆盖 ServicesBinding 的 handleSystemMessage 被调用
@override
Future handleSystemMessage(Object systemMessage) async {
await super.handleSystemMessage(systemMessage);
final Map message = systemMessage as Map;
final String type = message['type'] as String;
switch (type) {
case 'fontsChange':
_systemFonts.notifyListeners();
break;
}
return;
}
PaintingBinding.handleSystemMessage 优先调用 super.handleSystemMessage ,也就是先调用 ServicesBinding.handleSystemMessage异步处理系统发送的内存紧张信号,接着异步处理系统字体变动事件。
1.4 PaintingBinding
除了前面讲的监听系统字体变化事件,这里主要是在绘制热身帧之前预热Skia渲染引擎
@override
void initInstances() {
super.initInstances();
_instance = this;
// 初始化图片缓存
_imageCache = createImageCache();
if (shaderWarmUp != null) {
//第一帧绘制前的预热工作
shaderWarmUp.execute();
}
}
1.5 SemanticsBinding
渲染辅助类绑定,主要负责关联语义树与Flutter Engine。Flutter维护了一个 semantic tree(语义树),页面构建的时候会根据各Widget的语义描述构建一棵 semantic tree。如在Image组件中配置 semanticLabel 语义内容,用户在IOS/Android手机开启无障碍功能时,触摸到该 Image 时通过语义树查找到对应的语义描述交给Flutter Engine,实现读屏等功能。
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = window.accessibilityFeatures;
}
1.6 RendererBinding
渲染绑定,RendererBinding是render tree 与 Flutter engine的粘合剂,它持有了render tree的根节点 renderView
@override
void initInstances() {
super.initInstances();
_instance = this;
// 初始化PipelineOwner管理渲染流程
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
// 设置window的屏幕参数变化、文本缩放因子变化、亮度等变化、语义启用等回调。
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
// 初始化一个RenderView作为render tree的根节点,作为渲染执行入口
initRenderView();
// 设置是否根据render tree生成语义树
_handleSemanticsEnabledChanged();
assert(renderView != null);
// 绘制回调
addPersistentFrameCallback(_handlePersistentFrameCallback);
// 初始化鼠标监听
initMouseTracker();
}
GestureBinding.initInstances 方法中的事件处理,调用的就是这里的renderView.hitTest 从根节点开始命中测试的。正因为 RenderBinding 创建并持有了 RenderView 实例,所以 GestureBinding 中通过 mixin 机制将 RenderBinding 的 hitTest 方法混入,从而可以实现命中测试,相当于需要用到命中测试的地方都通过 mixin 委托给 RenderBinding 来实现了。
addPersistentFrameCallback 将绘制处理回调_handlePersistentFrameCallback 加入到 FrameCallback 类型回调列表,_handlePersistentFrameCallback 中的 drawFrame 实现绘制流水线。
WidgetsBinding
组件绑定
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
// 初始化BuildOwnder,处理需要绘制的Element的构建工作
_buildOwner = BuildOwner();
// 通过SchedulerBinding初始化window的onBeginFrame、onDrawFrame回调
// 如果app可见,通过window.scheduleFrame向engine发起绘制请求
buildOwner.onBuildScheduled = _handleBuildScheduled;
// 语言环境变化处理
window.onLocaleChanged = handleLocaleChanged;
// platform访问权限变化处理
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
// 处理系统发送的push/pop页面请求
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
WidgetsBinding属于最外层的 mixin,作为处理 Widget 相关事件的入口。在初始化过程中主要是生成了 BuildOwner 实例,以及window的onBeginFrame、onDrawFrame 回调,后面渲染流程会用到。
BindingBase先通过按顺序执行7个mixin的initInstances方法,完成了相关初始化工作,以及两个重要类的实例化PipelineOwner、BuildOwner。
然后就是执行了initServiceExtensions方法,实现了该方法的mixin按调用顺序为WidgetsBinding-->RendererBinding-->SchedulerBinding-->ServicesBinding主要就是在debug模式下注册相关拓展服务。
2 绑定根节点(scheduleAttachRootWidget)
由于是组件相关,scheduleAttachRootWidget 具体的实现在WidgetsBinding 里
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
// 将传入的Widget绑定到一个根节点并构建三棵树
attachRootWidget(rootWidget);
});
}
将app的主widget(即传入的 rootWidget)和根节点绑定。其中render tree 的根节点就是前面初始化流程中RendererBinding.initInstances过程创建的RenderView,RenderView是继承自RenderObject的,所以还需要创建Element和Widget与之关联,而创建的 Element 和 Widget 分别对应另外两棵树的根节点。
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement);
}
先是通过传入的 rootWidget 及 RenderView 实例化了一个RenderObjectToWidgetAdapter对象,而RenderObjectToWidgetAdapter是继承自RenderObjectWidget,即创建了Widget树的根节点。继续调用 attachToRenderTree
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
if (element == null) {
owner.lockState(() {
// 创建了一个RenderObjectToWidgetElement实例作为element tree的根节点
element = createElement();
assert(element != null);
// 绑定BuildOwner
element.assignOwner(owner);
});
// 标记需要构建的element,并rebuild
owner.buildScope(element, () {
element.mount(null, null);
});
SchedulerBinding.instance.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
@override
RenderObjectToWidgetElement createElement() => RenderObjectToWidgetElement(this);
attachToRenderTree 中通过 createElement() 创建了一个RenderObjectToWidgetElement 实例作为 element tree 的根节点,并绑定BuildOwner,通过 BuildOwner 构建需要构建的 element。
3 绘制热身帧(scheduleWarmUpFrame)
绑定完根节点后,就开始立即执行scheduleWarmUpFrame()绘制热身帧。不用绘制热身帧也可以渲染的,那为什么还需要绘制热身帧?
绘制热身帧的目的是:
- 前面 window.scheduleFrame 发起绘制请求是在收到Vsync信号后才开始的,app初始化时为了节省时间并未等待Vsync信号直接开始绘制,最多可以节省16.6ms(60Hz屏幕刷新率)等待时间。
- 在热身帧绘制结束前通过加锁来屏蔽期间的屏幕指针事件处理及_taskQueue中的回调,保证在绘制过程中不会再触发新的重绘。
- 在热身帧绘制结束后调用 resetEpoch() 来重置时间戳,避免热重载情况从热身帧到热重载帧的时间差,导致隐式动画的跳帧情况。
和普通绘制一样,热身帧也是通过handleBeginFrame、handleDrawFrame这两个回调来进行绘制流程,在前面 WidgetBinding 初始化时将这两个回调交给了window,具体代码逻辑是在 SchedulerBinding。
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// Timer任务会加入到event queue
// 所以在执行绘制前先处理完microtask queue中的任务
Timer.run(() {
assert(_warmUpFrame);
// 绘制Frame前工作,主要是处理Animate动画
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
// 开始Frame绘制
handleDrawFrame();
// 重置时间戳
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
// 后续Frame绘制请求
scheduleFrame();
});
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
handleBeginFrame处理动画相关逻辑,动画回调后并不立即执行动画,而是改变了animation.value,并调用setSate()来发起绘制请求。动画的过程就是在 Vsync 信号到来时根据动画进度计算出对应的 value,而对应的 Widget 也会随着 animation.value 的变化而重建,从而形成动画,和Android的属性动画原理差不多。
handleBeginFrame处理完后,会优先处理microTask任务队列。然后才是event Task,window.onDrawFrame(),对应SchedulerBinding.handleDrawFrame()。(Timer任务会加入到event queue,flutter的事件处理机制是优先处理 micro queue 中任务)
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// 处理Persistent类型回调,主要包括build\layout\draw流程
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
// 注释1
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// 处理Post-Frame回调,主要是状态清理,准备调度下一帧绘制请求
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List localPostFrameCallbacks =
List.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
// 处理完成,设置状态为idle
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
assert(() {
if (debugPrintEndFrameBanner)
debugPrint('▀' * _debugBanner.length);
_debugBanner = null;
return true;
}());
_currentFrameTimeStamp = null;
}
}
上面代码中的注释1处的 _invokeFrameCallback 中处理的 callback 就是我们上面在 RendererBinding 中的 被添加进 FrameCallback 类型的列表中的 _handlePersistentFrameCallback,_handlePersistentFrameCallback 里面调用了 drawFrame,这里也是用到了 mixin 机制,在 WidgetsBinding.drawFrame() 中完成组件的构建任务,在 RendererBinding.drawFrame 完成组件的布局、绘制任务
// RendererBinding
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_mouseTracker.schedulePostFrameCheck();
}
void drawFrame() {
assert(renderView != null);
// 布局
pipelineOwner.flushLayout();
// 更新 RenderObject 中需要绘制的内容
pipelineOwner.flushCompositingBits();
// 绘制
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
// 产生这一帧的数据Scene,由window.render交给Engine,最终显示到屏幕(发送数据到GPU)。
renderView.compositeFrame();
// 将语义树发送到操作系统
pipelineOwner.flushSemantics();
_firstFrameSent = true;
}
}
// WidgetsBinding
void drawFrame() {
...
try {
if (renderViewElement != null)
//调用BuildOwner.buildScope开始构建
buildOwner.buildScope(renderViewElement);
//调用RendererBinding.drawFrame,开始布局、绘制阶段。
super.drawFrame();
//从element tree中移除不需要的element,unmount
buildOwner.finalizeTree();
} finally {
...
}
}
绘制流程结束后会调用renderView.compositeFrame()
产生这一帧的数据 Scene,由 window.render 交给Engine,最终显示到屏幕。整个热身帧绘制流程:
SchedulerBinding.scheduleWarmUpFrame
-> SchedulerBinding.handleBeginFrame 处理动画
-> SchedulerBinding.handleDrawFrame
-----> WidgetBinding.drawFrame 通过 buildOwner 构建组件
-----> RendererBinding.drawFrame 通过 pipelineOwner 完成组件布局和绘制
-----> renderView.compositeFrame 发送 Scene 到GPU
总结
mixin机制在FlutterApp启动过程带来的优势:
- 高内聚低耦合:适合应用于需要多个功能模块配合完成的场景,将功能模块通过mixin解耦,各模块职责单一,相互之间不直接引用。
- 代码复用:多个类可通过混入 mixin 类来复用 mixin 类的代码
- 保证调用顺序:mixin配合super调用,可以实现同名方法的“继承链”式调用,保证串行执行顺序。
Flutter App的启动过程总结:
- ensureInitialized 通过7个 mixin 类 按顺序完成相关初始化工作
- scheduleAttachRootWidget 绑定app 应用启动的 Widget 到 render tree 的根节点RenderView上并生成widget tree 的根节点 RenderObjectToWidgetAdapter,RenderView又关联了widget tree 的根节点和 element tree 的根节点
- scheduleWarmUpFrame 完成热身帧绘制
参考文章:
从mixin机制理解Flutter App启动