注册

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:处理渲染相关。




  • WidgetsBindingWidgets相关。




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 是桥接RenderObjectElement的,Element是持有Widget的具体实现,通过RenderObject进行渲染,也就是通过这个方法实现了 Widget、Element、RenderObject的初始及绑定关系。


..scheduleWarmUpFrame();


绑定之后,接下来就是将内容显示在屏幕上,从以下代码分别调用了handleBeginFramehandleDrawFrame方法,通过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
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册