1. <output id="5rrzn"></output>

        <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>
        1. Flutter启动流程初探

          PHP技术大全 / 2019-03-15 15:03:17

          最近开始研究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(
          container: renderView,
          debugShortDescription: '[root]',
          child: rootWidget
          ).attachToRenderTree(buildOwner, renderViewElement);
          }

          attachRootWidget方法中,通过RenderObjectToWidgetAdapter的attachToRenderTree去创建顶层视图的element(renderViewElement)。

          attachToRenderTree

          RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement 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方法将其?#20197;?#21040;视图树上。

          createElement

          @override
          RenderObjectToWidgetElement createElement() => RenderObjectToWidgetElement(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 widget) : super(widget);

          这个是它的构造函数,可以看到上文提到的widget就是通过构造函数传进来的。

          @override
          RenderObjectToWidgetElement createElement() => RenderObjectToWidgetElement(this);

          这个是RenderObjectToWidgetAdapter构造RenderObjectToWidgetElement的方法。有次我们得知,上文提到的widget就是RenderObjectToWidgetAdapter。那么updateChild中的widget.child就是RenderObjectToWidgetAdapter的child变量。

          回到最开始的attachRootWidget方法:

          void attachRootWidget(Widget rootWidget) {
          _renderViewElement = RenderObjectToWidgetAdapter(
          container: renderView,
          debugShortDescription: '[root]',
          child: rootWidget
          ).attachToRenderTree(buildOwner, renderViewElement);
          }

          非常清楚了,RenderObjectToWidgetAdapter的child变量就是我们runApp中传进来的rootWidget,也就是例子中的MyApp。

          总结一下

          ?#26053;?#25105;们来总结一下上面的流程:

          1. Flutter通过runApp方法启动整个应用。

          2. runApp中通过attachRootWidget方法创建顶层视图的element,而这个element是RenderObjectToWidgetElement。

          3. 创建完毕之后调用element的mount方法?#20197;亍?/p>

          4. mount方法最后,会调用runApp中传进来的业务rootWidget的createElement方法创建element。

          5. 最后调用element的mount方法。

          遍历树

          上文最后我们得知,根视图element的mount方法最终会调用业务根视图的element的mount方法,那?#27425;?#20204;带入我们的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之后,遍历?#20197;?#23436;了整个视图树,通过scheduleWarmUpFrame方法去渲染,具体逻辑之后有机会再深究吧~

          干货分享

          敬请关注“PHP技术大全”微信公众号


          足球外围投注app

              1. <output id="5rrzn"></output>

                <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>

                      1. <output id="5rrzn"></output>

                        <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>