Flutter - 深入理解Flutter应用启动

基于Flutter 1.5,从源码视角来深入剖析flutter应用的启动流程,相关源码目录见文末附录

一、概述

上一篇文章深入理解Flutter引擎启动 已经介绍了FlutterApplication和FlutterActivity的onCreate()方法执行过程, 并触发Flutter引擎的启动,并最终执行到runApp(Widget app)方法,这才刚刚开始执行dart的业务代码,本文便从该方法讲起说一说flutter应用内逻辑的执行流程。

1.1 起点

1void main() => runApp(MyApp()); 2 3class MyApp extends StatelessWidget { 4 @override 5 Widget build(BuildContext context) { 6 return MaterialApp( 7 title: 'Gityuan Flutter Demo', 8 theme: ThemeData( 9 primarySwatch: Colors.blue, 10 ), 11 home: GityuanHomePage(title: 'Gityuan Flutter Demo Home Page'), 12 ); 13 } 14}

这是一段flutter app的demo代码,可见root widget就是MyApp对象。

1.2 启动流程图

1) runApp启动流程图

RunApp

1.3 相关类图

1) Widget/Element/RenderObject类图

ClassTree

Flutter中有3个比较重要的树Widget/Element/RenderObject,可以看出Widget/Element继承于共同的父类DiagnosticableTree,RenderObject继承于AbstractNode父类。 这3颗树之间是什么关系,如何相互关联,接下来会详细展开,并在文末会有更详细的类关系图。

二、应用启动流程

2.1 runApp

[-> lib/src/widgets/binding.dart]

1void runApp(Widget app) { 2 WidgetsFlutterBinding.ensureInitialized() //[见小节2.2] 3 ..attachRootWidget(app) //[见小节2.4] 4 ..scheduleWarmUpFrame(); //[见小节2.7] 5}

2.2 ensureInitialized

[-> lib/src/widgets/binding.dart]

1class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { 2 3 static WidgetsBinding ensureInitialized() { 4 if (WidgetsBinding.instance == null) 5 WidgetsFlutterBinding(); //首次初始化,先初始化父类BindingBase [小节2.2.1] 6 return WidgetsBinding.instance; 7 } 8}

WidgetsFlutterBinding这是一个单例模式,负责创建WidgetsFlutterBinding对象,WidgetsFlutterBinding继承抽象类BindingBase,并且附带7个mixin,对于mixin语法来说顺序是很重要,相同的方法会由后面的mixin覆盖前面的mixin方法,类关系图如下:

ClassBinding

图解:

  • BindingBase:抽象基类
  • WidgetsBinding:绑定组件树
  • RendererBinding:绑定渲染树
  • SemanticsBinding:绑定语义树
  • PaintingBinding:绑定绘制操作
  • SchedulerBinding:绑定帧绘制回调函数,以及widget生命周期相关事件
  • ServicesBinding:绑定平台服务消息,注册Dart层和C++层的消息传输服务;
  • GestureBinding:绑定手势事件,用于检测应用的各种手势相关操作;

2.2.1 BindingBase初始化

[-> lib/src/foundation/binding.dart]

1abstract class BindingBase { 2 BindingBase() { 3 developer.Timeline.startSync('Framework initialization'); 4 5 initInstances(); //初始化状态 [见小节2.3] 6 initServiceExtensions(); //扩展调试相关服务 7 8 developer.postEvent('Flutter.FrameworkInitialization', <String, String>{}); 9 developer.Timeline.finishSync(); 10 } 11}

该方法的主要工作:

  • 执行initInstances()方法会依次调用到每一下mixin来初始化一些状态;
  • 执行initServiceExtensions方法会依次调用WidgetsBinding,RendererBinding,SchedulerBinding,ServicesBinding,BindingBase这5个类,主要是根据不同的包(release/profile/debug)调用registerServiceExtension()方法来注册各种扩展服务用于debug。

2.3 initInstances

mixin对于with的顺序有关,initInstances首先执行的便是最右边的WidgetsBinding类中的initInstances()方法,由于该方法都包含super.initInstances(),故会依次调用以下这些mixin的initInstances()方法进行初始化,调用栈如下图:

widget_binding_init

2.3.1 WidgetsBinding.initInstances

[-> lib/src/widgets/binding.dart]

1mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 2 final BuildOwner _buildOwner = BuildOwner(); 3 Element _renderViewElement; 4 5 void initInstances() { 6 super.initInstances(); 7 _instance = this; 8 buildOwner.onBuildScheduled = _handleBuildScheduled; 9 ui.window.onLocaleChanged = handleLocaleChanged; 10 ui.window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; 11 //处理导航的方法回调 12 SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation); 13 //处理系统消息,比如内存紧张 14 SystemChannels.system.setMessageHandler(_handleSystemMessage); 15 } 16}

WidgetsBinding初始化过程会设置window的如下回调方法:

  • onLocaleChanged
  • onAccessibilityFeaturesChanged

2.3.2 RendererBinding.initInstances

[-> lib/src/rendering/binding.dart]

1mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, SemanticsBinding, HitTestable { 2 PipelineOwner _pipelineOwner; 3 RenderView get renderView => _pipelineOwner.rootNode; 4 5 void initInstances() { 6 super.initInstances(); 7 _instance = this; 8 //创建PipelineOwner对象 9 _pipelineOwner = PipelineOwner( 10 onNeedVisualUpdate: ensureVisualUpdate, 11 onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, 12 onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, 13 ); 14 ui.window 15 ..onMetricsChanged = handleMetricsChanged 16 ..onTextScaleFactorChanged = handleTextScaleFactorChanged 17 ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged 18 ..onSemanticsAction = _handleSemanticsAction; 19 initRenderView(); //初始化RenderView,见下文 20 _handleSemanticsEnabledChanged(); 21 addPersistentFrameCallback(_handlePersistentFrameCallback); 22 } 23}

RendererBinding初始化过程,创建PipelineOwner、RenderView对象以及设置window的如下回调方法:

  • onMetricsChanged
  • onTextScaleFactorChanged
  • onSemanticsEnabledChanged
  • onSemanticsAction
1void initRenderView() { 2 renderView = RenderView(configuration: createViewConfiguration(), window: window); 3 renderView.scheduleInitialFrame(); //初始化首帧 4} 5 6set renderView(RenderView value) { 7 _pipelineOwner.rootNode = value; 8}

创建对象RenderView,并保持到_pipelineOwner.rootNode里面,可通过RendererBinding.renderView获取。

2.3.3 SemanticsBinding

[-> lib/src/semantics/binding.dart]

1mixin SemanticsBinding on BindingBase { 2 static SemanticsBinding _instance; 3 4 void initInstances() { 5 super.initInstances(); 6 _instance = this; 7 _accessibilityFeatures = window.accessibilityFeatures; 8 } 9}

2.3.4 PaintingBinding

[-> lib/src/painting/binding.dart]

1mixin PaintingBinding on BindingBase, ServicesBinding { 2 static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp(); 3 4 void initInstances() { 5 super.initInstances(); 6 _instance = this; 7 _imageCache = createImageCache(); //创建image缓存 8 if (shaderWarmUp != null) { 9 shaderWarmUp.execute(); 10 } 11 } 12}

该过程会创建image缓存。

2.3.5 SchedulerBinding

[-> lib/src/scheduler/binding.dart]

1mixin SchedulerBinding on BindingBase, ServicesBinding { 2 void initInstances() { 3 super.initInstances(); 4 _instance = this; 5 window.onBeginFrame = _handleBeginFrame; 6 window.onDrawFrame = _handleDrawFrame; 7 SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); 8 readInitialLifecycleStateFromNativeWindow(); 9 } 10}

SchedulerBinding初始化过程,设置生命周期回调以及设置window的如下回调方法:

  • onMetricsChanged
  • onBeginFrame
  • onDrawFrame

2.3.6 ServicesBinding

[-> lib/src/services/binding.dart]

1mixin ServicesBinding on BindingBase { 2 void initInstances() { 3 super.initInstances(); 4 _instance = this; 5 window 6 ..onPlatformMessage = BinaryMessages.handlePlatformMessage; 7 initLicenses(); 8 } 9}

ServicesBinding初始化过程,window的如下回调方法:

  • onPlatformMessage

2.3.7 GestureBinding

[-> lib/src/gestures/binding.dart]

1mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget { 2 void initInstances() { 3 super.initInstances(); //这里调用到BindingBase类,该类是个空方法 4 _instance = this; 5 window.onPointerDataPacket = _handlePointerDataPacket; 6 }

GestureBinding初始化过程,window的如下回调方法:

  • onPointerDataPacket

创建完WidgetsBinding,接下来执行其attachRootWidget()方法

2.4 WidgetsBinding.attachRootWidget

[-> lib/src/widgets/binding.dart]

1mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { 2 Element get renderViewElement => _renderViewElement; 3 Element _renderViewElement; 4 5 void attachRootWidget(Widget rootWidget) { 6 //创建桥接对象[见小节2.4.1] 7 _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( 8 container: renderView, 9 debugShortDescription: '[root]', 10 child: rootWidget 11 ).attachToRenderTree(buildOwner, renderViewElement); //[见小节2.5] 12 } 13}

该方法所涉及的变量:

  • rootWidget: 是指flutter应用自定义的根Widget,由开发者自定义;
  • renderView: 指由RendererBinding初始化过程创建的RenderView(该类继承于RenderObject),见小节[2.3.2];
  • _renderViewElement: 是由attachToRenderTree()执行后的返回值,类型为Element子类;

可通过WidgetsBinding的renderViewElement来获取创建的RenderObjectToWidgetElement对象。

2.4.1 RenderObjectToWidgetAdapter

[-> lib/src/widgets/binding.dart]

1class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget { 2 final Widget child; 3 final RenderObjectWithChildMixin<T> container; 4 final String debugShortDescription; //用于调试:简短描述该widget 5 6 RenderObjectToWidgetAdapter({ 7 this.child, 8 this.container, 9 this.debugShortDescription, 10 }) : super(key: GlobalObjectKey(container)); 11}

RenderObjectToWidgetAdapter是从[RenderObject]到[Element]树的一个桥接对象,该类的核心方法有:

  • createElement:返回的是RenderObjectToWidgetElement类型;
  • createRenderObject:返回的便是这个container,也就是renderView;

2.5 attachToRenderTree

[-> lib/src/widgets/binding.dart ::RenderObjectToWidgetAdapter]

1class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget { 2 3 RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); 4 5 RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) { 6 if (element == null) { 7 owner.lockState(() { 8 element = createElement(); //创建Element对象,见小节2.5.1 9 element.assignOwner(owner); //将owner赋值给element成员_owner 10 }); 11 owner.buildScope(element, () { //执行build过程,见小节2.5.2 12 element.mount(null, null); 13 } else { 14 element._newWidget = this; 15 element.markNeedsBuild(); 16 } 17 return element; 18 } 19 20}

首次调用attachToRenderTree时,element为空则会创建RenderObjectToWidgetElement对象,如果再次调用则会复用已创建的element对象。

2.5.1 createElement

[-> lib/src/widgets/binding.dart ::RenderObjectToWidgetAdapter]

RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); 

此处this便是RenderObjectToWidgetAdapter对象,该过程会将RenderObjectToWidgetAdapter保存在父类Element的widget成员变量中。

2.5.2 BuildOwner.buildScope

[-> lib/src/widgets/framework.dart]

1void buildScope(Element context, [VoidCallback callback]) { 2 if (callback == null && _dirtyElements.isEmpty) 3 return; 4 Timeline.startSync('Build', arguments: timelineWhitelistArguments); 5 try { 6 if (callback != null) { 7 _dirtyElementsNeedsResorting = false; 8 callback(); //此处回调为mount() ,见小节2.5.3 9 } 10 ... 11 _dirtyElements.sort(Element._sort); 12 int dirtyCount = _dirtyElements.length; 13 while (index < dirtyCount) { 14 _dirtyElements[index].rebuild(); //针对脏元素执行rebuild操作 15 ... 16 } 17 } finally { 18 ... 19 Timeline.finishSync(); 20 } 21}

2.5.3 RenderObjectToWidgetElement.mount

[-> lib/src/widgets/binding.dart]

1void mount(Element parent, dynamic newSlot) { 2 super.mount(parent, newSlot); //[见小节2.5.4] 3 _rebuild(); //[见小节2.6] 4}

2.5.4 RenderObjectElement.mount

[-> lib/src/widgets/framework.dart]

1abstract class RenderObjectElement extends Element { 2 3 void mount(Element parent, dynamic newSlot) { 4 super.mount(parent, newSlot); //[见小节2.5.5] 5 _renderObject = widget.createRenderObject(this); 6 attachRenderObject(newSlot); //将newSlot依附到RenderObject上 7 _dirty = false; 8 } 9}

此处的widget是RenderObjectToWidgetAdapter对象,那么_renderObject返回的便是renderView对象;

2.5.5 Element.mount

[-> lib/src/widgets/framework.dart]

1abstract class Element extends DiagnosticableTree implements BuildContext { 2 void mount(Element parent, dynamic newSlot) { 3 _parent = parent; 4 _slot = newSlot; 5 _depth = _parent != null ? _parent.depth + 1 : 1; 6 _active = true; 7 if (parent != null) 8 _owner = parent.owner; 9 if (widget.key is GlobalKey) { 10 final GlobalKey key = widget.key; 11 key._register(this); //注册并建立globalKey和Element的关联 12 } 13 _updateInheritance(); 14 } 15}

在前面小节[2.5.3]在执行完mount操作后,便会开始执行下面的_rebuild()方法。

2.6 RenderObjectToWidgetElement._rebuild

[-> lib/src/widgets/binding.dart]

1void _rebuild() { 2 try { 3 //[见小节2.6.1] 4 _child = updateChild(_child, widget.child, _rootChildSlot); 5 } catch (exception, stack) { 6 ... 7 } 8}

2.6.1 Element.updateChild

[-> lib/src/widgets/framework.dart]

1Element updateChild(Element child, Widget newWidget, dynamic newSlot) { 2 if (newWidget == null) { 3 if (child != null) 4 deactivateChild(child); //移除旧的子元素 5 return null; 6 } 7 if (child != null) { 8 if (child.widget == newWidget) { 9 if (child.slot != newSlot) 10 updateSlotForChild(child, newSlot); //更新子元素在父级所用的slot 11 return child; 12 } 13 if (Widget.canUpdate(child.widget, newWidget)) { 14 if (child.slot != newSlot) 15 updateSlotForChild(child, newSlot); 16 child.update(newWidget); 17 return child; 18 } 19 deactivateChild(child); 20 } 21 return inflateWidget(newWidget, newSlot); //为给定的widget创建新Element [见小节2.6.2] 22}

2.6.2 Element.inflateWidget

[-> lib/src/widgets/framework.dart]

1Element inflateWidget(Widget newWidget, dynamic newSlot) { 2 final Key key = newWidget.key; 3 if (key is GlobalKey) { 4 //从_inactiveElements中查找是否有可复用的Element 5 final Element newChild = _retakeInactiveElement(key, newWidget); 6 if (newChild != null) { 7 newChild._activateWithParent(this, newSlot); 8 //找到可复用Element,则从相应列表移除,并更新子视图 9 final Element updatedChild = updateChild(newChild, newWidget, newSlot); 10 return updatedChild; 11 } 12 } 13 //否则创建新的Element 14 final Element newChild = newWidget.createElement(); 15 newChild.mount(this, newSlot); 16 return newChild; 17}

2.7 SchedulerBinding.scheduleWarmUpFrame

[-> lib/src/scheduler/binding.dart]

1void scheduleWarmUpFrame() { 2 // _warmUpFrame用于保证不会再没有绘制完成的情况下,再次回调绘制方法 3 if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) 4 return; 5 6 _warmUpFrame = true; 7 Timeline.startSync('Warm-up frame'); 8 final bool hadScheduledFrame = _hasScheduledFrame; 9 Timer.run(() { 10 //使用定时器执行帧准备绘制的方法 11 handleBeginFrame(null); 12 }); 13 Timer.run(() { 14 //使用定时器执行帧绘制的方法 15 handleDrawFrame(); 16 //这帧结束后重置时间 17 resetEpoch(); 18 _warmUpFrame = false; 19 if (hadScheduledFrame) 20 scheduleFrame(); 21 }); 22 23 lockEvents(() async { 24 await endOfFrame; 25 Timeline.finishSync(); 26 }); 27}

预热帧结束后调用resetEpoch,以便在热重载情况下,下一帧假装在预热帧之后立刻发生。 由于预热帧的时间戳通常会在过去(最后一个真实帧的时间),如果不重置时间,则会出现从预热帧中的旧时间突然跳转到 “真实”框架中的新时间。 最大的问题就是隐式动画最终会在过去被触发,然后跳过每一帧并在新时间结束,从而出现帧跳跃的情况。

另外关于handleBeginFrame和handleDrawFrame的渲染绘制过程,见文章Flutter渲染机制—UI线程中的第四Framework层处理流程部分。

三、小结

runApp(MyApp)是flutter应用开始真正执行业务逻辑代码的起点,整个过程主要工作:

  • WidgetsFlutterBinding初始化:这是一个单例模式,负责创建WidgetsFlutterBinding对象,WidgetsFlutterBinding继承抽象类BindingBase,并且附带7个mixin,初始化渲染、语义化、绘制、平台消息以及手势等一系列操作;
  • attachRootWidget:遍历挂载整个视图树,并建立Widget、Element、RenderObject之间的连接与关系,此处Element的具体类型为RenderObjectToWidgetElement;
  • scheduleWarmUpFrame:调度预热帧,执行帧绘制方法handleBeginFrame和handleDrawFrame。

Widget/Element/RenderObject类图

ClassTreeDet

从WidgetsFlutterBinding是单例模式,从小节[2.4]得WidgetsBinding的renderViewElement记录着唯一的RenderObjectToWidgetElement对象,从小节[2.3.2]可知RendererBinding的renderView记录着唯一的RenderView对象;也就是说每个flutter应用创建的Root Widget跟Element、RenderObject一一对应,且单例唯一。

MyApp是用户定义的根Widget,为了建立三棵树的关系,RenderObjectToWidgetAdapter起到重要的桥接功能,该类的createElement方法创建RenderObjectToWidgetElement对象,createRenderObject()方法获取的是RenderView。


微信公众号 Gityuan | 微博 weibo.com/gityuan | 博客 留言区交流


本文转自 http://gityuan.com/2019/06/29/flutter_run_app/,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Flutter - Flutter渲染机制—GPU线程

基于Flutter1.5,从源码视角来深入剖析flutter渲染机制,相关源码目录见文末附录一、GPU线程渲染看Flutter的渲染绘制过程的核心过程包括在ui线程和gpu线程,上一篇文章已经详细介绍了UI线程的工作原理,本文则介绍GPU线程的工作原理,这里需要注意的是,gpu线程是指运行着GPUTaskRunner的名叫gpu

Flutter - 深入理解Flutter引擎启动

基于Flutter1.5,从源码视角来深入剖析flutter引擎的启动流程,相关源码目录见文末附录一、Flutter引擎启动工作1.1Flutter启动概览Flutter作为一款跨平台的框架,可以运行在Android、iOS等平台,Android为例讲解如何从Android应用启动流程中衔接到Flutter框架,