内容简介:最近开始研究Flutter了,俗话说工欲善其事必先利其器,在正式运用Flutter之前肯定要先了解了解它的工作机制,于是开始了Flutter以及Dart的源码学习之旅,这次就简单的分析一下Flutter的启动流程,作为记录~上面是官网上一个demo的一部分,我们可以看到其中有一个main函数,内部使用了runApp并将业务视图顶层的MyApp传了进去。这样我们的Flutter界面就能展示出来了。既然如此,我们就来看runApp的源码吧,内部逻辑很简单,就是通过WidgetsFlutterBinding去初始
最近开始研究Flutter了,俗话说工欲善其事必先利其器,在正式运用Flutter之前肯定要先了解了解它的工作机制,于是开始了Flutter以及Dart的源码学习之旅,这次就简单的分析一下Flutter的启动流程,作为记录~
hello world
void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } }
上面是官网上一个demo的一部分,我们可以看到其中有一个main函数,内部使用了runApp并将业务视图顶层的MyApp传了进去。这样我们的Flutter界面就能展示出来了。
runApp
void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..attachRootWidget(app) ..scheduleWarmUpFrame(); }
既然如此,我们就来看runApp的源码吧,内部逻辑很简单,就是通过WidgetsFlutterBinding去初始化一些逻辑(ensureInitialized)方法,然后调用attachRootWidget并将我们的MyApp组件传入。
attachRootWidget
void attachRootWidget(Widget rootWidget) { _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( container: renderView, debugShortDescription: '[root]', child: rootWidget ).attachToRenderTree(buildOwner, renderViewElement); }
attachRootWidget方法中,通过RenderObjectToWidgetAdapter的attachToRenderTree去创建顶层视图的element(renderViewElement)。
attachToRenderTree
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) { if (element == null) { owner.lockState(() { element = createElement(); assert(element != null); element.assignOwner(owner); }); owner.buildScope(element, () { element.mount(null, null); }); } else { element._newWidget = this; element.markNeedsBuild(); } return element; }
这个方法就很有讲究了,首先如果element是空,则调用createElement方法去创建,然后通过mount方法将其挂载到视图树上。
createElement
@override RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
createElement方法就是创建了一个RenderObjectToWidgetElement。由此我们知道,Flutter顶层视图的element就是这个RenderObjectToWidgetElement。然后我们再来看它的mount方法。
mount
@override void mount(Element parent, dynamic newSlot) { assert(parent == null); super.mount(parent, newSlot); _rebuild(); }
void _rebuild() { try { _child = updateChild(_child, widget.child, _rootChildSlot); assert(_child != null); } catch (exception, stack) { final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'widgets library', context: 'attaching to the render tree' ); FlutterError.reportError(details); final Widget error = ErrorWidget.builder(details); _child = updateChild(null, error, _rootChildSlot); } }
@protected Element updateChild(Element child, Widget newWidget, dynamic newSlot) { assert(() { if (newWidget != null && newWidget.key is GlobalKey) { final GlobalKey key = newWidget.key; key._debugReserveFor(this); } return true; }()); if (newWidget == null) { if (child != null) deactivateChild(child); return null; } if (child != null) { if (child.widget == newWidget) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); assert(child.widget == newWidget); assert(() { child.owner._debugElementWasRebuilt(child); return true; }()); return child; } deactivateChild(child); assert(child._parent == null); } return inflateWidget(newWidget, newSlot); }
@protected Element inflateWidget(Widget newWidget, dynamic newSlot) { assert(newWidget != null); final Key key = newWidget.key; if (key is GlobalKey) { final Element newChild = _retakeInactiveElement(key, newWidget); if (newChild != null) { assert(newChild._parent == null); assert(() { _debugCheckForCycles(newChild); return true; }()); newChild._activateWithParent(this, newSlot); final Element updatedChild = updateChild(newChild, newWidget, newSlot); assert(newChild == updatedChild); return updatedChild; } } final Element newChild = newWidget.createElement(); assert(() { _debugCheckForCycles(newChild); return true; }()); newChild.mount(this, newSlot); assert(newChild._debugLifecycleState == _ElementLifecycle.active); return newChild; }
可以看到mount方法最终调用了inflateWidget方法。inflateWidget方法中,通过newWidget的createElement方法创建了子element并调用其mount方法。
这个newWidget是什么呢?
_child = updateChild(_child, widget.child, _rootChildSlot);
这个是前面讲到的顶层element RenderObjectToWidgetElement的_rebuild,可以看到newWidget相对应传入的是widget.child,那么这个widget又是什么呢?我们继续往前推,看一下RenderObjectToWidgetElement是如果构造的。
RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);
这个是它的构造函数,可以看到上文提到的widget就是通过构造函数传进来的。
@override RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
这个是RenderObjectToWidgetAdapter构造RenderObjectToWidgetElement的方法。有次我们得知,上文提到的widget就是RenderObjectToWidgetAdapter。那么updateChild中的widget.child就是RenderObjectToWidgetAdapter的child变量。
回到最开始的attachRootWidget方法:
void attachRootWidget(Widget rootWidget) { _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( container: renderView, debugShortDescription: '[root]', child: rootWidget ).attachToRenderTree(buildOwner, renderViewElement); }
非常清楚了,RenderObjectToWidgetAdapter的child变量就是我们runApp中传进来的rootWidget,也就是例子中的MyApp。
总结一下
下面我们来总结一下上面的流程:
- Flutter通过runApp方法启动整个应用。
- runApp中通过attachRootWidget方法创建顶层视图的element,而这个element是RenderObjectToWidgetElement。
- 创建完毕之后调用element的mount方法挂载。
- mount方法最后,会调用runApp中传进来的业务rootWidget的createElement方法创建element。
- 最后调用element的mount方法。
遍历树
上文最后我们得知,根视图element的mount方法最终会调用业务根视图的element的mount方法,那么我们带入我们的demo,业务根视图MyApp是继承自StatelessWidget。
abstract class StatelessWidget extends Widget { @override StatelessElement createElement() => StatelessElement(this); }
它的createElement方法创建了一个StatelessElement。
class StatelessElement extends ComponentElement { /// Creates an element that uses the given widget as its configuration. StatelessElement(StatelessWidget widget) : super(widget); @override StatelessWidget get widget => super.widget; @override Widget build() => widget.build(this); @override void update(StatelessWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _dirty = true; rebuild(); } }
StatelessElement继承自ComponentElement,我们来看一下ComponentElement的mount方法。
@override void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); assert(_child == null); assert(_active); _firstBuild(); assert(_child != null); }
void _firstBuild() { rebuild(); }
void rebuild() { assert(_debugLifecycleState != _ElementLifecycle.initial); if (!_active || !_dirty) return; assert(() { if (debugOnRebuildDirtyWidget != null) { debugOnRebuildDirtyWidget(this, _debugBuiltOnce); } if (debugPrintRebuildDirtyWidgets) { if (!_debugBuiltOnce) { debugPrint('Building $this'); _debugBuiltOnce = true; } else { debugPrint('Rebuilding $this'); } } return true; }()); assert(_debugLifecycleState == _ElementLifecycle.active); assert(owner._debugStateLocked); Element debugPreviousBuildTarget; assert(() { debugPreviousBuildTarget = owner._debugCurrentBuildTarget; owner._debugCurrentBuildTarget = this; return true; }()); performRebuild(); assert(() { assert(owner._debugCurrentBuildTarget == this); owner._debugCurrentBuildTarget = debugPreviousBuildTarget; return true; }()); assert(!_dirty); }
@override void performRebuild() { assert(() { if (debugProfileBuildsEnabled) Timeline.startSync('${widget.runtimeType}', arguments: timelineWhitelistArguments); return true; }()); assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true)); Widget built; try { built = build(); debugWidgetBuilderValue(widget, built); } catch (e, stack) { built = ErrorWidget.builder(_debugReportException('building $this', e, stack)); } finally { // We delay marking the element as clean until after calling build() so // that attempts to markNeedsBuild() during build() will be ignored. _dirty = false; assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false)); } try { _child = updateChild(_child, built, slot); assert(_child != null); } catch (e, stack) { built = ErrorWidget.builder(_debugReportException('building $this', e, stack)); _child = updateChild(null, built, slot); } assert(() { if (debugProfileBuildsEnabled) Timeline.finishSync(); return true; }()); }
最终调用了performRebuild方法,而在这个方法中:
_child = updateChild(_child, built, slot);
又回去调用前文提到的updateChild方法,这样就做到了遍历整个视图树,创建视图了。
最后
最后,runApp中我们还有一个方法没有提到:
void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..attachRootWidget(app) ..scheduleWarmUpFrame(); }
那就是scheduleWarmUpFrame,该方法在attachRootWidget之后,遍历挂载完了整个视图树,通过scheduleWarmUpFrame方法去渲染,具体逻辑之后有机会再深究吧~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms and Data Structures
Kurt Mehlhorn、Peter Sanders / Springer / 2008-08-06 / USD 49.95
Algorithms are at the heart of every nontrivial computer application, and algorithmics is a modern and active area of computer science. Every computer scientist and every professional programmer shoul......一起来看看 《Algorithms and Data Structures》 这本书的介绍吧!