Flutter入口中的runApp方法解析
前言
开发中,如果在runApp
方法执行之前设置Android沉浸式样式报错,需要先设置WidgetsFlutterBinding.ensureInitialized();
这一行代码才行,为什么,接下来看下这一行代码具体做了啥。
点进去发现这个方法在runApp
中进行了实现,并且还调用了WidgetsFlutterBinding
的另两个方法,
方法体:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
接下来我们对这三个方法进行一个一个进行分析。
1、WidgetsFlutterBinding.ensureInitialized()
代码:
/// widgets框架的具体绑定,将框架绑定到Flutter引擎的中间层。
/// A concrete binding for applications based on the Widgets framework.
/// This is the glue that binds the framework to the Flutter engine.
class WidgetsFlutterBinding extends BindingBase with GestureBinding,
SchedulerBinding, ServicesBinding, PaintingBinding,
SemanticsBinding, RendererBinding, WidgetsBinding {
/// 只有需要绑定时,才需要调用这个方法,在runApp之前调用。
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
可以看到,WidgetsFlutterBinding
继承了 BindingBase
,并混入了一些其他Binding
。
先看下BindingBase
类,
/// 初始化 获取唯一window
ui.SingletonFlutterWindow get window => ui.window;
/// 初始化PlatformDispatcher实例,平台消息和配置的中心入口点,负责分发事件给Flutter引擎。
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
主要是获取了window
实例和PlatformDispatcher
实例。
再看下其他Binding
解释:
GestureBinding
:处理手势相关。SchedulerBinding
: 处理系统调度。ServicesBinding
:处理与原生的交互。PaintingBinding
:处理绘制相关。SemanticsBinding
:处理语义化。RendererBinding
:处理渲染相关。WidgetsBinding
:Widgets
相关。
Flutter框架层的相关基础绑定。
接着我们看下改变状态栏的代码。
改变样式核心代码:
if (_pendingStyle != _latestStyle) {
// 通过和原生平台进行通信 来改变具体平台状态样式
SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setSystemUIOverlayStyle',
_pendingStyle!._toMap(),
);
_latestStyle = _pendingStyle;
}
通过 ensureInitialized
的注释和修改样式的代码即可解决我们开头的疑问,因为设置状态栏样式是通过原生window
窗口进行修改的,所以这里如果需要修改状态栏,就需要进行和原生绑定才能拿到原生的window
窗口来进行修改。
从注释来看:WidgetsFlutterBinding
是widgets框架的具体绑定,将框架绑定到Flutter引擎的中间层。
通过 ensureInitialized
方法返回 一个WidgetsBinding
单例类。
至此,WidgetsFlutterBinding.ensureInitialized();的工作已经结束。
就是做了初始化引擎绑定,返回WidgetsBinding
。
..scheduleAttachRootWidget(app)
上一个方法我们知道返回了WidgetsBinding
类,那这个方法就是在WidgetsBinding
这个类里,接下来先看下这个类。
/// widgets和Flutter引擎之间的粘合剂,中间层
/// The glue between the widgets layer and the Flutter engine.
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
// 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;
/// 略
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
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();
}
}
可以看到这是一个mixin类,创建了BuildOwner
的实例,这个类是用来管理Element
的,在attachRootWidget
方法中创建了RenderObjectToWidgetAdapter
实例,并设置了我们的runApp中的参数根节点rootWigdt
。
/// 桥接 RenderObject 到 Element
/// A bridge from a [RenderObject] to an [Element] tree.
RenderObjectToWidgetAdapter({
this.child,
required this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
RenderObjectToWidgetAdapter
是桥接RenderObject
到Element
的,Element
是持有Widget
的具体实现,通过RenderObject
进行渲染,也就是通过这个方法实现了 Widget、Element、RenderObject
的初始及绑定关系。
..scheduleWarmUpFrame();
绑定之后,接下来就是将内容显示在屏幕上,从以下代码分别调用了handleBeginFrame
、handleDrawFrame
方法,通过hadScheduledFrame
判断是否调用handleBeginFrame
触发scheduleFrame
方法,调用 window.scheduleFrame();
最终调用 platformDispatcher.scheduleFrame();
通知引擎在合适的时机进行帧绘制。
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
final TimelineTask timelineTask = TimelineTask()..start('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();
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
void scheduleFrame() {
window.scheduleFrame();
}
/// Requests that, at the next appropriate opportunity, the [onBeginFrame] and
/// [onDrawFrame] callbacks be invoked.
void scheduleFrame() => platformDispatcher.scheduleFrame();
小结
runApp中的三个方法执行的三步分别是:
1、初始化WidgetsFlutterBinding
返回WidgetBinding
实例。
2、初始化Widget、Elment、RenderObject
三棵树并确定绑定关系。
3、通知引擎合适时机进行帧绘制。更快的将内容显示到屏幕中。
作者:老李code
链接:https://juejin.cn/post/7098218181604409381
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。