Flutter
程式的入口main()
方法會呼叫runApp()
方法,我們本篇探索runApp
都做了啥。
概覽
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
複製程式碼
這個方法一看很簡潔,一共呼叫了WidgetsFlutterBinding
的三個方法,在看三個方法的實現之前,我們看一下WidgetsFlutterBinding
是什麼。
WidgetsFlutterBinding
我們看一下官方的解釋:
A concrete binding for applications based on the Widgets framework.This is the glue that binds the framework to the Flutter engine.
翻譯過來就是,一個基於Widgets framework
的應用程式的具體繫結,它是繫結framework
和Flutter engine
的膠水層。
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
複製程式碼
它的父類BindingBase
是一個抽象類,with
實現了很多mixin
,這些mixin
只能用於繼承自BindingBase
的類。mixin
的作用是擴充套件功能,mixin
可以類比於iOS
的protocol
(個人見解,如果不對歡迎指正)。
BindingBase
abstract class BindingBase {
/// 省略程式碼
/// However, multiple window support is not yet implemented, so currently this
/// provides access to the one and only window.
// TODO(gspencergoog): remove the preceding note once multi-window support is
// active.
// 唯一的window
ui.SingletonFlutterWindow get window => ui.window;
/// 每一個BindingBase類定義行為 都有一個 platformDispatcher 作為回撥(handlers)
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
/// 初始化例項
void initInstances() {
assert(!_debugInitialized);
assert(() {
_debugInitialized = true;
return true;
}());
}
/// The current [WidgetsBinding], if one has been created.
/// ensureInitialized方法返回的例項
/// If you need the binding to be constructed before calling [runApp],
/// you can ensure a Widget binding has been constructed by calling the
/// `WidgetsFlutterBinding.ensureInitialized()` function.
static WidgetsBinding? get instance => _instance;
static WidgetsBinding? _instance;
/// 註冊 service extensions 初始化之後呼叫
void initServiceExtensions() {
///省略程式碼
}
}
複製程式碼
ui.window
:是Flutter App
顯示的視窗,它繼承自FlutterView
,位於Flutter engine
層。ui.PlatformDispatcher.instance
:platformDispatcher是Flutter 的一個事件分發器,負責Flutter分發engine的事件,和傳遞事件給engine層。initInstances
:初始化例項的方法。initServiceExtensions()
:註冊service extensions
,比如platformOverride
、activeDevToolsServerAddress
等。
ensureInitialized()方法
該方法的作用是返回一個WidgetsBinding
型別例項,如果未建立就新建立一個。
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
複製程式碼
就是返回一個WidgetsBinding.instance
例項,因為WidgetsFlutterBinding
實現了GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding
mixin,mixin的initInstances
和initServiceExtensions
方法也會呼叫,每個mixin的功能:
GestureBinding
:處理手勢。SchedulerBinding
: 處理系統排程。ServicesBinding
:處理與原生的互動。PaintingBinding
:處理繪製。SemanticsBinding
:處理語義化。RendererBinding
:處理渲染。WidgetsBinding
:Widgets
相關。
我們下面主要看WidgetsBinding
和RendererBinding
。
WidgetsBinding
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
// Initialization of [_buildOwner] has to be done after
// [super.initInstances] is called, as it requires [ServicesBinding] to
// properly setup the [defaultBinaryMessenger] instance.
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
///省略程式碼
}
複製程式碼
WidgetsBinding
初始化會建立一個BuildOwner
物件,它的作用是管理Widget
樹和Element
樹。
RendererBinding
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
if (kIsWeb) {
addPostFrameCallback(_handleWebFirstFrame);
}
}
/// The current [RendererBinding], if one has been created.
static RendererBinding? get instance => _instance;
static RendererBinding? _instance;
///省略程式碼
}
複製程式碼
RendererBinding
初始化會建立一個PipelineOwner
物件,用於管理RenderObject
樹。PipelineOwner
和BuildOwner
都位於framework
層,它們通過Bingding
(膠水層)與engine
互動。
- 初始化了一個PipelineOwner用於管理RenderObject.
- 將
_handlePersistentFrameCallback
這個callback傳入SchedulerBinding
中的_postFrameCallbacks
中,這樣在硬體每次發出VSync
訊號的時候都會呼叫RenderBinding
中的_handlePersistentFrameCallback
方法._handlePersistentFrameCallback
方法中直接呼叫了drawFrame
方法。
scheduleAttachRootWidget
例項化之後會呼叫scheduleAttachRootWidget
方法。
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
複製程式碼
呼叫了attachRootWidget
方法:
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance!.ensureVisualUpdate();
}
}
複製程式碼
attachRootWidget方法用於是為根Widget生成一個根Element。生成Element呼叫了attachToRenderTree方法並傳入了BuildOwner和Element。
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
///新建立一個 element
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
// 建立能更新widget 樹的能力,可以回撥 callback,構建所有標記為dirty的elment
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
複製程式碼
這個方法主要是element
為空的時候新建一個element
,新建後會呼叫BuildOwner
的buildScope
主要作用是建立能更新widget
樹的能力,可以回撥 callback
,構建所有標記為dirty
的element
。
attachRootWidget
方法,最後會執行SchedulerBinding.instance!.ensureVisualUpdate()
,
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
複製程式碼
它主要是呼叫新的幀的排程管理。它會呼叫scheduleFrame
方法
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
///給window設定回撥
ensureFrameCallbacksRegistered();
///排程更新
window.scheduleFrame();
_hasScheduledFrame = true;
}
複製程式碼
這裡會給window
設定onBeginFrame
和onDrawFrame
的回撥,window
會把回撥傳給platformDispatcher
。
@override
set onBeginFrame(ui.FrameCallback? callback) {
platformDispatcher.onBeginFrame = callback;
}
@override
ui.VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame;
@override
set onDrawFrame(ui.VoidCallback? callback) {
platformDispatcher.onDrawFrame = callback;
}
複製程式碼
也就是說scheduleAttachRootWidget
經過一系列呼叫之後,會把SchedulerBinding
的_handleBeginFrame
和_handleDrawFrame
傳給platformDispatcher
。platformDispatcher
分發來自enginee的事件。而在這裡SingletonFlutterWindow
把platformDispatcher
的onBeginFrame
和onDrawFrame
這兩個事件交給SchedulerBinding
處理。
當硬體發出VSync
訊號時,會呼叫platformDispatcher的onDrawFrame
。實際上會呼叫SchedulerBinding
中的_handleDrawFrame
方法。_handleDrawFrame
會呼叫handleDrawFrame
方法:
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
} finally {
///省略程式碼
}
}
複製程式碼
_postFrameCallbacks
裡面儲存的是callback
,作用是硬體每次發出VSync
訊號的時候都會呼叫。這裡的_postFrameCallbacks
是在RenderBinding
這個mixin
的initInstances
方法中傳入的
addPersistentFrameCallback(_handlePersistentFrameCallback);
複製程式碼
scheduleWarmUpFrame
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
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);
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
複製程式碼
這個方法主要呼叫是scheduleFrame
,跟進程式碼實際是呼叫的window.scheduleFrame()
,
@override
void scheduleFrame() {
platformDispatcher.scheduleFrame();
}
複製程式碼
window.scheduleFrame()
呼叫了platformDispatcher.scheduleFrame()
,通知engine
層需要繪製。engine會根據情況儘快地呼叫platformDispatcher的onDrawFrame方法。
總結
runApp方法主要做了以下事情:
- 建立
WidgetsFlutterBinding
它是連線framework
和engine
的膠水層。註冊Vsync
回撥,後面每一幀的呼叫會出發WidgetsFlutterBinding
的回撥,最後傳遞到framework
層處理邏輯。 attachRootWidget
:遍歷掛載整個檢視樹,建立widget
、element
、renderObjcect
的連線關係。scheduleWarmUpFrame
:排程幀預熱(warmUp)。執行幀繪製handleBeginFrame
和handleDrawFrame
方法。