Flutter启动流程初探

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

内容简介:最近开始研究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方法去渲染,具体逻辑之后有机会再深究吧~


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

查看所有标签

猜你喜欢:

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

解构产品经理:互联网产品策划入门宝典

解构产品经理:互联网产品策划入门宝典

电子工业出版社 / 2018-1 / 65

《解构产品经理:互联网产品策划入门宝典》以作者丰富的职业背景及著名互联网公司的工作经验为基础,从基本概念、方法论和工具的解构入手,配合大量正面或负面的案例,完整、详细、生动地讲述了一个互联网产品经理入门所需的基础知识。同时,在此基础上,将这些知识拓展出互联网产品策划的领域,融入日常工作生活中,以求职、沟通等场景为例,引导读者将知识升华为思维方式。 《解构产品经理:互联网产品策划入门宝典》适合......一起来看看 《解构产品经理:互联网产品策划入门宝典》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换