提示:本文設(shè)計(jì)到的 Flutter framework 層源碼是基于 Flutter 1.20.0
void main() => runApp(MyApp());
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
三行代碼代表了Flutter APP 啟動(dòng)的三個(gè)主流程:
- binding初始化(ensureInitialized)
- 綁定根節(jié)點(diǎn)(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()其實(shí)就是一個(gè)獲取WidgetsFlutterBinding單例的過(guò)程,真正的初始化實(shí)現(xiàn)代碼在其7個(gè)mixin中。7個(gè)mixin按照嚴(yán)格的先后調(diào)用鏈關(guān)系完成不同 binding 的初始化。
WidgetsFlutterBinding繼承了BindingBase,在調(diào)用自己的構(gòu)造器之前會(huì)先先執(zhí)行了父類(lèi)BindingBase構(gòu)造函數(shù)。
BindingBase() {
developer.Timeline.startSync('Framework initialization');
assert(!_debugInitialized);
initInstances();
assert(_debugInitialized);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
developer.Timeline.finishSync();
}
這里會(huì)調(diào)用initInstances() ,由于7個(gè) mixin 都重寫(xiě)了initInstances(), with 的最后一個(gè) WidgetsBinding 覆蓋了前面的 binding 的 initInstances(),所以 WidgetsBinding 的 initInstances() 會(huì)首先被調(diào)用,而 WidgetsBinding 的 initInstances 函數(shù)中先通過(guò) super 向上調(diào)用 initInstances ,所以 initInstances 的執(zhí)行順序依次是:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBinding -> WidgetsBinding,從而依次完成各個(gè) Binding 的初始化相關(guān)工作。
1.1 GestureBinding
手勢(shì)事件綁定,主要處理觸屏幕指針事件的分發(fā)以及事件最終回調(diào)處理。
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;
}
這里將事件處理回調(diào) _handlePointerDataPacket 賦值給 window,供window 收到屏幕指針事件后調(diào)用。window類(lèi)是framework層與engine層處理屏幕相關(guān)事件的橋梁。_handlePointerDataPacket中會(huì)先調(diào)用hitTest進(jìn)行命中測(cè)試。GestureBinding及RenderBinding都實(shí)現(xiàn)了hitTest方法,按照mixin順序會(huì)優(yōu)先調(diào)用RenderBinding.hitTest。RenderBinding.hitTest會(huì)從renderTree的根節(jié)點(diǎn)遞歸調(diào)用命中測(cè)試,返回命中的深度最大的節(jié)點(diǎn)到根節(jié)點(diǎn)路徑上的所有節(jié)點(diǎn)。然后再執(zhí)行dispatchEvent根據(jù)返回的hitTest命中節(jié)點(diǎn)列表遍歷分發(fā)事件,事件分發(fā)的順序是先子節(jié)點(diǎn)后父節(jié)點(diǎn)最終到根節(jié)點(diǎn),類(lèi)似Android的事件冒泡機(jī)制。
1.2 SchedulerBinding
繪制調(diào)度綁定
@override
void initInstances() {
super.initInstances();
_instance = this;
// debug編譯模式時(shí)統(tǒng)計(jì)繪制流程時(shí)長(zhǎng),開(kāi)始、運(yùn)行、構(gòu)建、光柵化。
if (!kReleaseMode) {
int frameNumber = 0;
addTimingsCallback((List<FrameTiming> timings) {
for (final FrameTiming frameTiming in timings) {
frameNumber += 1;
_profileFramePostEvent(frameNumber, frameTiming);
}
});
}
}
SchedulerBinding的作用就是在debug編譯模式時(shí)統(tǒng)計(jì)繪制流程時(shí)長(zhǎng),開(kāi)始、運(yùn)行、構(gòu)建、光柵化。
1.3 ServicesBinding
@override
void initInstances() {
super.initInstances();
_instance = this;
// 構(gòu)建一個(gè)用于platform與flutter層通信的 BinaryMessenger
_defaultBinaryMessenger = createBinaryMessenger();
// 設(shè)置window監(jiān)聽(tīng)回調(diào),處理platform發(fā)送的消息
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLicenses();
// 設(shè)置處理platform發(fā)送的系統(tǒng)消息的 Handler
SystemChannels.system.setMessageHandler(handleSystemMessage);
// 設(shè)置AppLifecycleState生命周期回調(diào)
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
// AppLifecycleState 為 resumed 和 inactive 時(shí)才允許響應(yīng)Vsync信號(hào)進(jìn)行繪制
readInitialLifecycleStateFromNativeWindow();
}
ServicesBinding的初始化的工作主要是兩個(gè):
- platform與flutter層通信相關(guān)服務(wù)的初始化
- 注冊(cè)監(jiān)聽(tīng)了flutter app的生命周期變化事件,根據(jù)生命周期狀態(tài)決定是否允許發(fā)起繪制任務(wù)
設(shè)置處理 system 消息 handleSystemMessage,ServicesBinding 的handleSystemMessage 主要工作是異步處理系統(tǒng)發(fā)送的內(nèi)存緊張信號(hào),PaintingBinding 也實(shí)現(xiàn)了該方法。由于 PaintingBinding 被 with 的順序在 ServicesBinding 后面,所以 PaintingBinding 的 handleSystemMessage 會(huì)覆蓋 ServicesBinding 的 handleSystemMessage 被調(diào)用
@override
Future<void> handleSystemMessage(Object systemMessage) async {
await super.handleSystemMessage(systemMessage);
final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
final String type = message['type'] as String;
switch (type) {
case 'fontsChange':
_systemFonts.notifyListeners();
break;
}
return;
}
PaintingBinding.handleSystemMessage 優(yōu)先調(diào)用 super.handleSystemMessage ,也就是先調(diào)用 ServicesBinding.handleSystemMessage異步處理系統(tǒng)發(fā)送的內(nèi)存緊張信號(hào),接著異步處理系統(tǒng)字體變動(dòng)事件。
1.4 PaintingBinding
除了前面講的監(jiān)聽(tīng)系統(tǒng)字體變化事件,這里主要是在繪制熱身幀之前預(yù)熱Skia渲染引擎
@override
void initInstances() {
super.initInstances();
_instance = this;
// 初始化圖片緩存
_imageCache = createImageCache();
if (shaderWarmUp != null) {
//第一幀繪制前的預(yù)熱工作
shaderWarmUp.execute();
}
}
1.5 SemanticsBinding
渲染輔助類(lèi)綁定,主要負(fù)責(zé)關(guān)聯(lián)語(yǔ)義樹(shù)與Flutter Engine。Flutter維護(hù)了一個(gè) semantic tree(語(yǔ)義樹(shù)),頁(yè)面構(gòu)建的時(shí)候會(huì)根據(jù)各Widget的語(yǔ)義描述構(gòu)建一棵 semantic tree。如在Image組件中配置 semanticLabel 語(yǔ)義內(nèi)容,用戶在IOS/Android手機(jī)開(kāi)啟無(wú)障礙功能時(shí),觸摸到該 Image 時(shí)通過(guò)語(yǔ)義樹(shù)查找到對(duì)應(yīng)的語(yǔ)義描述交給Flutter Engine,實(shí)現(xiàn)讀屏等功能。
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = window.accessibilityFeatures;
}
1.6 RendererBinding
渲染綁定,RendererBinding是render tree 與 Flutter engine的粘合劑,它持有了render tree的根節(jié)點(diǎn) renderView
@override
void initInstances() {
super.initInstances();
_instance = this;
// 初始化PipelineOwner管理渲染流程
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
// 設(shè)置window的屏幕參數(shù)變化、文本縮放因子變化、亮度等變化、語(yǔ)義啟用等回調(diào)。
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
// 初始化一個(gè)RenderView作為render tree的根節(jié)點(diǎn),作為渲染執(zhí)行入口
initRenderView();
// 設(shè)置是否根據(jù)render tree生成語(yǔ)義樹(shù)
_handleSemanticsEnabledChanged();
assert(renderView != null);
// 繪制回調(diào)
addPersistentFrameCallback(_handlePersistentFrameCallback);
// 初始化鼠標(biāo)監(jiān)聽(tīng)
initMouseTracker();
}
GestureBinding.initInstances 方法中的事件處理,調(diào)用的就是這里的renderView.hitTest 從根節(jié)點(diǎn)開(kāi)始命中測(cè)試的。正因?yàn)?RenderBinding 創(chuàng)建并持有了 RenderView 實(shí)例,所以 GestureBinding 中通過(guò) mixin 機(jī)制將 RenderBinding 的 hitTest 方法混入,從而可以實(shí)現(xiàn)命中測(cè)試,相當(dāng)于需要用到命中測(cè)試的地方都通過(guò) mixin 委托給 RenderBinding 來(lái)實(shí)現(xiàn)了。
addPersistentFrameCallback 將繪制處理回調(diào)_handlePersistentFrameCallback 加入到 FrameCallback 類(lèi)型回調(diào)列表,_handlePersistentFrameCallback 中的 drawFrame 實(shí)現(xiàn)繪制流水線。
WidgetsBinding
組件綁定
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
// 初始化BuildOwnder,處理需要繪制的Element的構(gòu)建工作
_buildOwner = BuildOwner();
// 通過(guò)SchedulerBinding初始化window的onBeginFrame、onDrawFrame回調(diào)
// 如果app可見(jiàn),通過(guò)window.scheduleFrame向engine發(fā)起繪制請(qǐng)求
buildOwner.onBuildScheduled = _handleBuildScheduled;
// 語(yǔ)言環(huán)境變化處理
window.onLocaleChanged = handleLocaleChanged;
// platform訪問(wèn)權(quán)限變化處理
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
// 處理系統(tǒng)發(fā)送的push/pop頁(yè)面請(qǐng)求
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
WidgetsBinding屬于最外層的 mixin,作為處理 Widget 相關(guān)事件的入口。在初始化過(guò)程中主要是生成了 BuildOwner 實(shí)例,以及window的onBeginFrame、onDrawFrame 回調(diào),后面渲染流程會(huì)用到。
BindingBase先通過(guò)按順序執(zhí)行7個(gè)mixin的initInstances方法,完成了相關(guān)初始化工作,以及兩個(gè)重要類(lèi)的實(shí)例化PipelineOwner、BuildOwner。
然后就是執(zhí)行了initServiceExtensions方法,實(shí)現(xiàn)了該方法的mixin按調(diào)用順序?yàn)閃idgetsBinding-->RendererBinding-->SchedulerBinding-->ServicesBinding主要就是在debug模式下注冊(cè)相關(guān)拓展服務(wù)。
2 綁定根節(jié)點(diǎn)(scheduleAttachRootWidget)
由于是組件相關(guān),scheduleAttachRootWidget 具體的實(shí)現(xiàn)在WidgetsBinding 里
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
// 將傳入的Widget綁定到一個(gè)根節(jié)點(diǎn)并構(gòu)建三棵樹(shù)
attachRootWidget(rootWidget);
});
}
將app的主widget(即傳入的 rootWidget)和根節(jié)點(diǎn)綁定。其中render tree 的根節(jié)點(diǎn)就是前面初始化流程中RendererBinding.initInstances過(guò)程創(chuàng)建的RenderView,RenderView是繼承自RenderObject的,所以還需要?jiǎng)?chuàng)建Element和Widget與之關(guān)聯(lián),而創(chuàng)建的 Element 和 Widget 分別對(duì)應(yīng)另外兩棵樹(shù)的根節(jié)點(diǎn)。
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
先是通過(guò)傳入的 rootWidget 及 RenderView 實(shí)例化了一個(gè)RenderObjectToWidgetAdapter對(duì)象,而RenderObjectToWidgetAdapter是繼承自RenderObjectWidget,即創(chuàng)建了Widget樹(shù)的根節(jié)點(diǎn)。繼續(xù)調(diào)用 attachToRenderTree
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
// 創(chuàng)建了一個(gè)RenderObjectToWidgetElement實(shí)例作為element tree的根節(jié)點(diǎn)
element = createElement();
assert(element != null);
// 綁定BuildOwner
element.assignOwner(owner);
});
// 標(biāo)記需要構(gòu)建的element,并rebuild
owner.buildScope(element, () {
element.mount(null, null);
});
SchedulerBinding.instance.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
attachToRenderTree 中通過(guò) createElement() 創(chuàng)建了一個(gè)RenderObjectToWidgetElement 實(shí)例作為 element tree 的根節(jié)點(diǎn),并綁定BuildOwner,通過(guò) BuildOwner 構(gòu)建需要構(gòu)建的 element。
3 繪制熱身幀(scheduleWarmUpFrame)
綁定完根節(jié)點(diǎn)后,就開(kāi)始立即執(zhí)行scheduleWarmUpFrame()繪制熱身幀。不用繪制熱身幀也可以渲染的,那為什么還需要繪制熱身幀?
繪制熱身幀的目的是:
- 前面 window.scheduleFrame 發(fā)起繪制請(qǐng)求是在收到Vsync信號(hào)后才開(kāi)始的,app初始化時(shí)為了節(jié)省時(shí)間并未等待Vsync信號(hào)直接開(kāi)始繪制,最多可以節(jié)省16.6ms(60Hz屏幕刷新率)等待時(shí)間。
- 在熱身幀繪制結(jié)束前通過(guò)加鎖來(lái)屏蔽期間的屏幕指針事件處理及_taskQueue中的回調(diào),保證在繪制過(guò)程中不會(huì)再觸發(fā)新的重繪。
- 在熱身幀繪制結(jié)束后調(diào)用 resetEpoch() 來(lái)重置時(shí)間戳,避免熱重載情況從熱身幀到熱重載幀的時(shí)間差,導(dǎo)致隱式動(dòng)畫(huà)的跳幀情況。
和普通繪制一樣,熱身幀也是通過(guò)handleBeginFrame、handleDrawFrame這兩個(gè)回調(diào)來(lái)進(jìn)行繪制流程,在前面 WidgetBinding 初始化時(shí)將這兩個(gè)回調(diào)交給了window,具體代碼邏輯是在 SchedulerBinding。
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// Timer任務(wù)會(huì)加入到event queue
// 所以在執(zhí)行繪制前先處理完microtask queue中的任務(wù)
Timer.run(() {
assert(_warmUpFrame);
// 繪制Frame前工作,主要是處理Animate動(dòng)畫(huà)
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
// 開(kāi)始Frame繪制
handleDrawFrame();
// 重置時(shí)間戳
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
// 后續(xù)Frame繪制請(qǐng)求
scheduleFrame();
});
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
handleBeginFrame處理動(dòng)畫(huà)相關(guān)邏輯,動(dòng)畫(huà)回調(diào)后并不立即執(zhí)行動(dòng)畫(huà),而是改變了animation.value,并調(diào)用setSate()來(lái)發(fā)起繪制請(qǐng)求。動(dòng)畫(huà)的過(guò)程就是在 Vsync 信號(hào)到來(lái)時(shí)根據(jù)動(dòng)畫(huà)進(jìn)度計(jì)算出對(duì)應(yīng)的 value,而對(duì)應(yīng)的 Widget 也會(huì)隨著 animation.value 的變化而重建,從而形成動(dòng)畫(huà),和Android的屬性動(dòng)畫(huà)原理差不多。
?
?handleBeginFrame處理完后,會(huì)優(yōu)先處理microTask任務(wù)隊(duì)列。然后才是event Task,window.onDrawFrame(),對(duì)應(yīng)SchedulerBinding.handleDrawFrame()。(Timer任務(wù)會(huì)加入到event queue,flutter的事件處理機(jī)制是優(yōu)先處理 micro queue 中任務(wù))
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// 處理Persistent類(lèi)型回調(diào),主要包括build\layout\draw流程
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
// 注釋1
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// 處理Post-Frame回調(diào),主要是狀態(tài)清理,準(zhǔn)備調(diào)度下一幀繪制請(qǐng)求
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
// 處理完成,設(shè)置狀態(tài)為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 中的 被添加進(jìn) FrameCallback 類(lèi)型的列表中的 _handlePersistentFrameCallback,_handlePersistentFrameCallback 里面調(diào)用了 drawFrame,這里也是用到了 mixin 機(jī)制,在 WidgetsBinding.drawFrame() 中完成組件的構(gòu)建任務(wù),在 RendererBinding.drawFrame 完成組件的布局、繪制任務(wù)
// RendererBinding
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_mouseTracker.schedulePostFrameCheck();
}
void drawFrame() {
assert(renderView != null);
// 布局
pipelineOwner.flushLayout();
// 更新 RenderObject 中需要繪制的內(nèi)容
pipelineOwner.flushCompositingBits();
// 繪制
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
// 產(chǎn)生這一幀的數(shù)據(jù)Scene,由window.render交給Engine,最終顯示到屏幕(發(fā)送數(shù)據(jù)到GPU)。
renderView.compositeFrame();
// 將語(yǔ)義樹(shù)發(fā)送到操作系統(tǒng)
pipelineOwner.flushSemantics();
_firstFrameSent = true;
}
}
// WidgetsBinding
void drawFrame() {
...
try {
if (renderViewElement != null)
//調(diào)用BuildOwner.buildScope開(kāi)始構(gòu)建
buildOwner.buildScope(renderViewElement);
//調(diào)用RendererBinding.drawFrame,開(kāi)始布局、繪制階段。
super.drawFrame();
//從element tree中移除不需要的element,unmount
buildOwner.finalizeTree();
} finally {
...
}
}
繪制流程結(jié)束后會(huì)調(diào)用renderView.compositeFrame()產(chǎn)生這一幀的數(shù)據(jù) Scene,由 window.render 交給Engine,最終顯示到屏幕。整個(gè)熱身幀繪制流程:
SchedulerBinding.scheduleWarmUpFrame
-> SchedulerBinding.handleBeginFrame 處理動(dòng)畫(huà)
-> SchedulerBinding.handleDrawFrame
-----> WidgetBinding.drawFrame 通過(guò) buildOwner 構(gòu)建組件
-----> RendererBinding.drawFrame 通過(guò) pipelineOwner 完成組件布局和繪制
-----> renderView.compositeFrame 發(fā)送 Scene 到GPU
總結(jié)
mixin機(jī)制在FlutterApp啟動(dòng)過(guò)程帶來(lái)的優(yōu)勢(shì):
- 高內(nèi)聚低耦合:適合應(yīng)用于需要多個(gè)功能模塊配合完成的場(chǎng)景,將功能模塊通過(guò)mixin解耦,各模塊職責(zé)單一,相互之間不直接引用。
- 代碼復(fù)用:多個(gè)類(lèi)可通過(guò)混入 mixin 類(lèi)來(lái)復(fù)用 mixin 類(lèi)的代碼
- 保證調(diào)用順序:mixin配合super調(diào)用,可以實(shí)現(xiàn)同名方法的“繼承鏈”式調(diào)用,保證串行執(zhí)行順序。
Flutter App的啟動(dòng)過(guò)程總結(jié):
- ensureInitialized 通過(guò)7個(gè) mixin 類(lèi) 按順序完成相關(guān)初始化工作
- scheduleAttachRootWidget 綁定app 應(yīng)用啟動(dòng)的 Widget 到 render tree 的根節(jié)點(diǎn)RenderView上并生成widget tree 的根節(jié)點(diǎn) RenderObjectToWidgetAdapter,RenderView又關(guān)聯(lián)了widget tree 的根節(jié)點(diǎn)和 element tree 的根節(jié)點(diǎn)
- scheduleWarmUpFrame 完成熱身幀繪制