Flutter Framework啟動程式碼閱讀
首先開啟啟動原生的應用,然後初始化flutter engine,之後在開啟4個Task Runners分別是
Platform Task Runner: 跟Flutter Engine的所有互動(介面呼叫)必須發生在Platform Thread,對應的native的主執行緒
UI Task Runner Thread(Dart Runner): UI Task Runner被Flutter Engine用於執行Dart root isolate程式碼
GPU Task Runner: 被用於執行裝置GPU的相關呼叫
FlutterEngine加之後, 開始執行main函式
IO Task Runner: 負責磁碟資料的讀取,主要是圖片,提前處理減輕GPU的壓力
下面主要是UI Task Runner
下的啟動過程,其他幾個TaskRunner大都在FlutterEngine層
Flutter main函式
它首先建立了一個MaterailApp
,通常會叫它根檢視,其實最終它會作為真正的根檢視的第一個child
然後執行WidgetsFlutterBinding,繫結flutterEngine的相關callback事件
構建ElementTree和RenderTree, WidgetTree在我們程式碼裡就能看到了,由開發者調整。它們的先後順序是 WidgetTree -> ElementTree -> RenderTree
然後通知Flutter Engine預熱,重新整理當前檢視,而不是等待vsync訊號
void main() {
runApp(
MaterialApp(
title: 'demo',
home: HomePage(),
),
);
}
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
1. 建立根RootWidget
1.1 這裡一般定義為MaterialApp, 它初始化了很多屬性它是一個使用material design的應用
const MaterialApp({
Key key,
this.navigatorKey,
this.home,
this.routes = const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onGenerateInitialRoutes,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.builder,
this.title = '',
this.onGenerateTitle,
this.color,
this.theme,
this.darkTheme,
this.themeMode = ThemeMode.system,
this.locale, //app的初始化local
this.localizationsDelegates,//設定app localizations的delegate,用於提供翻譯字串
this.localeListResolutionCallback,//app的local修改後的回撥
this.localeResolutionCallback,//用於攔截修改當前支援的local
this.supportedLocales = const <Locale>[Locale('en', 'US')],//指定app支援哪些local,不指定則採用系統的local
this.debugShowMaterialGrid = false,
this.showPerformanceOverlay = false,
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowCheckedModeBanner = true,
this.shortcuts,
this.actions,
})
1.2 MaterialApp中的資訊最終會傳遞給WdigetsApp, 具體實現在 _WidgetsAppState
_WidgetsAppState
@override
void initState() {
super.initState();
//建立GlobalKey,快取rootnavigator
_updateNavigator();
//獲取系統的local資訊,並回調給materialApp
_locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
WidgetsBinding.instance.addObserver(this);
}
//建立Router
Route<dynamic> _onGenerateRoute(RouteSettings settings)
...
widget.onGenerateRoute(settings);
//建立unknownRoot
Route<dynamic> _onUnknownRoute(RouteSettings settings) {
//監聽native的導航事件
Future<bool> didPopRoute() async //如果為android則為返回按鍵
Future<bool> didPushRoute(String route) async
//系統local處理
void didChangeLocales(List<Locale> locales) {
final Locale newLocale = _resolveLocales(locales, widget.supportedLocales);
//處理local
Locale _resolveLocales(List<Locale> preferredLocales, Iterable<Locale> supportedLocales) {
// 過濾local,用於修改local,如果取不到對應的local則採用相近的local
if (widget.localeListResolutionCallback != null) {
final Locale locale = widget.localeListResolutionCallback(preferredLocales, widget.supportedLocales);
//處理MaterialApp local回撥資訊,優先選擇系統的預設local
if (widget.localeResolutionCallback != null) {
final Locale locale = widget.localeResolutionCallback(
preferredLocales != null && preferredLocales.isNotEmpty ? preferredLocales.first : null,
widget.supportedLocales,
);
1.3 根檢視執行build方法, 構建Element的配置資訊
- 可以看到根檢視最終的Child的是一個
Navigator
, 而Navigator
會掛在一個視覺化的rootWidget
, 通過initialRoute去查詢。
Widget build(BuildContext context) {
Widget navigator;
if (_navigator != null) {
navigator = Navigator(
key: _navigator,
//優先選擇native的routeName
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
//設定route的構造方法
onGenerateRoute: _onGenerateRoute,
//設定initialRoute
onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
? Navigator.defaultGenerateInitialRoutes
: (NavigatorState navigator, String initialRouteName) {
return widget.onGenerateInitialRoutes(initialRouteName);
},
//設定位置的root
onUnknownRoute: _onUnknownRoute,
//設定navigator的觀察者
observers: widget.navigatorObservers,
);
}
Widget result;
if (widget.builder != null) {
result = Builder(
builder: (BuildContext context) {
return widget.builder(context, navigator);
},
);
} else {
assert(navigator != null);
result = navigator;
}
//設定樣式,DefaultTextStyle繼承於InheritedTheme
if (widget.textStyle != null) {
result = DefaultTextStyle(
style: widget.textStyle,
child: result,
);
}
...
Widget title;
if (widget.onGenerateTitle != null) {
//轉換child的context作用域,避免loaclizations widget構建失敗
title = Builder(
builder: (BuildContext context) {
final String title = widget.onGenerateTitle(context);
assert(title != null, 'onGenerateTitle must return a non-null String');
return Title(
title: title,
color: widget.color,
child: result,
);
},
);
} else {
title = Title(
title: widget.title,
color: widget.color,
child: result,
);
}
final Locale appLocale = widget.locale != null
? _resolveLocales(<Locale>[widget.locale], widget.supportedLocales)
: _locale;
...
//捕捉使用者意圖
return Shortcuts(
shortcuts: widget.shortcuts ?? WidgetsApp.defaultShortcuts,
debugLabel: '<Default WidgetsApp Shortcuts>',
child: Actions(
actions: widget.actions ?? WidgetsApp.defaultActions,
child: FocusTraversalGroup(
policy: ReadingOrderTraversalPolicy(),
//獲取native螢幕大小,繫結dart.ui window回撥資訊(textScale/brightness)
child: _MediaQueryFromWindow(
//通過引入_LocalizationsScope包裝根檢視 `Title` ,同時提供了local data給它的所有子widget
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
child: title,
),
),
),
),
);
}
2. 繫結FlutterEngine相關服務擴充套件
- 執行WidgetsFlutterBinding.ensureInitialized方法初始化, 在初始化方法中完成flutterEngine所有方法的繫結
- 建立全域性單例類
WidgetsFlutterBinding
, 對應程式碼如下
//WidgetsFlutterBinding初始化物件 `this` 賦值給了 `WidgetsBinding.instance` ,通過類屬性 `instance` 讓它成為了全域性單例子
WidgetsFlutterBinding();
return WidgetsBinding.instance;
WidgetsFlutterBinding關係圖如下,通過mixin實現多重繼承完成了FlutterEngine相關的事件繫結,它的繫結事件按照如下with從左到右的順序一次繫結,由此可見
WidgetsBindings依賴的
FlutterEngine`服務最多, 因此它需要最後再繫結.
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
2.1 BindingBase:
提供最基本的服務訂閱, 提供初始化 WidgetsBinding.instance
的方法
abstract class BindingBase {
BindingBase() {
//開啟一個同步執行的Timeline,初始化flutterWidgets執行所必須的的訂閱事件
//作為一個base class為子類提供斷言
developer.Timeline.startSync('Framework initialization');
...
//初始化mixin相關的例項物件
initInstances();
...
//提供便利方法,執行 flutter engine提供的 `ext.flutter.$name` 方法
initServiceExtensions();
...
//通知native,widgets初始化完畢,可以開始繪製
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
//解除Timeline的同步限制
developer.Timeline.finishSync();
}
//flutter engine提供的視窗,用於繫結native的service,監聽flutter engine的回撥事件
ui.Window get window => ui.window;
//定義一個抽象的方法,供mixin類初始化例項物件
@protected
@mustCallSuper
void initInstances() {
assert(() {
_debugInitialized = true;
return true;
}());
}
//註冊flutter engine的擴充套件資訊
@protected
@mustCallSuper
void initServiceExtensions() {
assert(!_debugServiceExtensionsRegistered);
assert(() {
//註冊dart vm的 hot reload事件,用於debug時快速更新app
registerSignalServiceExtension(
name: 'reassemble',
callback: reassembleApplication,
);
return true;
}());
if (!kReleaseMode && !kIsWeb) {
//註冊dart vm的退出事件
registerSignalServiceExtension(
name: 'exit',
callback: _exitApplication,
);
registerServiceExtension(
name: 'saveCompilationTrace',
callback: (Map<String, String> parameters) async {
return <String, dynamic>{
'value': ui.saveCompilationTrace(),
};
},
);
}
assert(() {
const String platformOverrideExtensionName = 'platformOverride';
//註冊platformOverride的回撥事件
registerServiceExtension(
name: platformOverrideExtensionName,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('value')) {
switch (parameters['value']) {
case 'android':
debugDefaultTargetPlatformOverride = TargetPlatform.android;
break;
case 'fuchsia':
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
break;
case 'iOS':
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
break;
case 'linux':
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
break;
case 'macOS':
debugDefaultTargetPlatformOverride = TargetPlatform.macOS;
break;
case 'windows':
debugDefaultTargetPlatformOverride = TargetPlatform.windows;
break;
case 'default':
default:
debugDefaultTargetPlatformOverride = null;
}
_postExtensionStateChangedEvent(
platformOverrideExtensionName,
defaultTargetPlatform.toString().substring('$TargetPlatform.'.length),
);
await reassembleApplication();
}
return <String, dynamic>{
'value': defaultTargetPlatform
.toString()
.substring('$TargetPlatform.'.length),
};
},
);
return true;
}());
assert(() {
_debugServiceExtensionsRegistered = true;
return true;
}());
}
//定義Timeline的同步執行事件,通過lock數量控制是否將解除timeline的同步執行
//對關鍵操作提供同步執行操作
@protected
Future<void> lockEvents(Future<void> callback()) {
developer.Timeline.startSync('Lock events');
//呼叫shell的退出命令,關閉程式
Future<void> _exitApplication() async {
exit(0);
}
2.2 GestureBinding
- 註冊手勢的點選事件回撥
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;
}
- 註冊MethodChannel的回撥
lifeCycle的監聽, frame重新整理
mixin ServicesBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
_defaultBinaryMessenger = createBinaryMessenger();
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLicenses();
SystemChannels.system.setMessageHandler(handleSystemMessage);
}
2.3 PaintingBinding
- 繫結繪製事件
mixin PaintingBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();
if (shaderWarmUp != null) {
shaderWarmUp.execute();
}
}
2.4 RendererBinding
- 繫結Render事件, 初始化根檢視的
RenderObject
, 它比較特殊, 不同於一般的RenderObject, 因為需要提供視窗的大小, 所以系統對它進行了一次包裝, 根據原始碼, 視窗的大小和deviceRatio就是在這個時候設定的
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
RenderView({
RenderBox child,
@required ViewConfiguration configuration,
@required ui.Window window,
})
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
//初始化渲染管道owner,繫結flutter engine的繪製檢視的回撥事件
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
//繫結flutter engine的 文字大小,測量資訊,螢幕亮度,語義行為事件
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
//初始化renderRootObject
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
//監聽系統的framecallback事件執行 `drawFrame`
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
}
WidgetsBindings
- 註冊flutter engine widget相關的事件
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
...
_buildOwner = BuildOwner();
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
3. 裝配RootWidget
- 在
FlutterEngine
相關的Service繫結以後, 開始執行scheduleAttachRootWidget
, 這裡通過Scheduler一個時間間隔為0的task來執行RootWidget繫結, Timer內部包裝了一個_RootZone的環境,
//在初始化的時候根據deviceRatio和螢幕大小初始化Root renderObject,賦值給`PiplineOwner`
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
set renderView(RenderView value) {
assert(value != null);
_pipelineOwner.rootNode = value;
}
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
...
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
//根據RootRenderObject和RootWidget建立真正的RootWidget
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView, //此處renderView是RenderBinding在初始化的時候根據視窗大小還有裝置畫素比建立,並賦值給`_pipelineOwner`
debugShortDescription: '[root]',
child: rootWidget, //runApp傳入的rootWidget
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
} // buildOwner是在WidgetBindings建立,用於管理ElementTree更新
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();//建立RootElment
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
// This is most likely the first time the framework is ready to produce
// a frame. Ensure that we are asked for one.
SchedulerBinding.instance.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
//每當element裝配到ElementTree中時建立RenderObject
abstract class RenderObjectElement extends Element {
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(() {
_debugDoingBuild = true;
return true;
}());
_renderObject = widget.createRenderObject(this);
...
//然後將renderObject插入到它的先組上面,如此迴圈,最終形成了ElementTree和RrenderTree, ElmentTree節點更觸發RenderTree節點的更新,Widget作為Element的配置資訊,觸發Element的更新。
@override
void attachRenderObject(dynamic newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
final ParentDataElement<ParentData> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
根據上面的執行順序可以看出, RootWidget
最先建立,然後是RootRenderObject
(PiplelineOwner負責管理RenderTree),
4. scheduleWarmUpFrame
- 向flutter主動請求重新整理,而不是被動的通過vsync重新整理
mixin SchedulerBinding on BindingBase, ServicesBinding {
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
//用於開啟一個同步事件,最終會提交一個event task到flutter engine
/**
// timeline.dart
external void _reportTaskEvent(int taskId, String phase, String category,
String name, String argumentsAsJson);
*/
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null); //通知flutter engine 重新整理
});
Timer.run(() {
...
handleDrawFrame();
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame(); // window.scheduleFrame(); -> void scheduleFrame() native 'Window_scheduleFrame';
});
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
總結
Flutter App在啟動時主要乾了以下幾件事情:
- 開啟main isolate,在預設的mainZone內運行了main函式.
- 建立MaterialApp(或者是自定義的rootWidget)
- 繫結flutter engine相關的服務,系統點選事件,鍵盤,螢幕,亮度,字型,繪製回撥,輔助提示,vsync資訊,system.platform channels ...,詳細參照啟動時Widgetsbinding的幾個mixin類的
initialServices
方法和dart.ui
- 構建RenderTree和ElementTree
- 主動通知flutter engine重新整理螢幕