Flutter启动流程初探

栏目: IOS · Android · 发布时间: 7年前

内容简介:最近开始研究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。

总结一下

下面我们来总结一下上面的流程:

  1. Flutter通过runApp方法启动整个应用。
  2. runApp中通过attachRootWidget方法创建顶层视图的element,而这个element是RenderObjectToWidgetElement。
  3. 创建完毕之后调用element的mount方法挂载。
  4. mount方法最后,会调用runApp中传进来的业务rootWidget的createElement方法创建element。
  5. 最后调用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方法去渲染,具体逻辑之后有机会再深究吧~


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Rework

Rework

Jason Fried、David Heinemeier Hansson / Crown Business / 2010-3-9 / USD 22.00

"Jason Fried and David Hansson follow their own advice in REWORK, laying bare the surprising philosophies at the core of 37signals' success and inspiring us to put them into practice. There's no jarg......一起来看看 《Rework》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具