Flutter源码阅读分析:Framework层的启动

Framework的启动


0. 前言

在我之前的文章Flutter源码阅读分析:引擎初始化与启动的最后,提到了在引擎启动时,会以“main”方法作为主入口函数,执行Dart代码。那么本片文章就从“main”方法着手,分析Dart Framework具体做了什么。

Framework代码:https://github.com/flutter/flutter


1. runApp

首先我们从flutter官方给的例子来看:

// ./examples/hello_world/lib/main.dart
void main() =>
  runApp( //[1]
    const Center( // [2]
      child:
        Text('Hello, world!',
          key: Key('title'),
          textDirection: TextDirection.ltr
        )
      )
    );
  • [1] runApp方法的主要功能是填充给定的Widget并将其附到屏幕上。
  • [2] Center是一种将子节点置于中心的Align,这些都是Fllutter的Widget系统,这个在后文中详细分析。

此处先看runApp做了什么:

// ./packages/flutter/lib/src/widgets/binging.dart
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

如果重复调用runApp方法,那么会将之前的根Widget从屏幕中移除,并将新的指定的Widget替换到该位置。这个新的Widget树与前者进行对比,并将区别的地方应用到后续的渲染树。这里涉及到Flutter的布局渲染机制,会在后续讲解。

WidgetsFlutterBinding类是基于Widget框架的应用程序的具体绑定。这将框架和Flutter引擎绑定起来。

// ./packages/flutter/lib/src/widgets/binding.dart
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }
}

ensureInitialized方法返回一个WidgetBinding的实例。
再看一下scheduleAttachRootWidgetscheduleWarmUpFrame方法:

// ./packages/flutter/lib/src/widgets/binding.dart
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  ...
  // 通过Timer安排一个任务,用于执行附着根Widget
  @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget); // [3]
    });
  }
  ...
}
// ./packages/flutter/lib/src/scheduler/binding.dart
mixin SchedulerBinding on BindingBase {
  ...
  // 以最快速度安排一帧,而不是等待引擎为响应系统“Vsync”信号而请求一帧。
  // 这个方法用于在应用启动时,这样第一帧可以得到多一些时间来运行。
  void scheduleWarmUpFrame() {
    ...
    // We use timers here to ensure that microtasks flush in between.
    Timer.run(() {
      handleBeginFrame(null); // [4]
    });
    Timer.run(() {
      handleDrawFrame(); // [5]
      resetEpoch();
      _warmUpFrame = false;
      if (hadScheduledFrame)
        scheduleFrame(); // [6]
    });
    ...
  }
  ...
}
  • [3] attachRootWidget方法将一个Widget附着到renderViewElement
// ./packages/flutter/lib/src/widgets/binding.dart
void attachRootWidget(Widget rootWidget) {
  _readyToProduceFrames = true;
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}

这里涉及到的WidgetElementRender,都属于Flutter的渲染机制,后续在渲染机制中详细分析。

  • [4] handleBeginFrame方法主要作用是让framework准备好,去创建一个新的帧。它会调用所有通过scheduleFrameCallback注册进来的临时回调函数。
  • [5] handleDrawFrame方法主要作用是创建一个新的帧。这个方法一般紧跟着handleBeginFrame方法后调用。
// ./packages/flutter/lib/src/scheduler/binding.dart
void handleDrawFrame() {
  ...
  // PERSISTENT FRAME CALLBACKS
  _schedulerPhase = SchedulerPhase.persistentCallbacks;
  for (final FrameCallback callback in _persistentCallbacks)
    _invokeFrameCallback(callback, _currentFrameTimeStamp);

  // POST-FRAME CALLBACKS
  _schedulerPhase = SchedulerPhase.postFrameCallbacks;
  final List<FrameCallback> localPostFrameCallbacks =
      List<FrameCallback>.from(_postFrameCallbacks);
  _postFrameCallbacks.clear();
  for (final FrameCallback callback in localPostFrameCallbacks)
    _invokeFrameCallback(callback, _currentFrameTimeStamp);
  ...
}

handleDrawFrame会调用所有通过addPersistentFrameCallback方法注册进来的回调函数(这些回调函数通常都是驱动渲染管线),以及通过addPostFrameCallback方法注册进来的回调函数。
RendererBinding类的initInstantces方法中调用了addPersistentFrameCallback方法。其添加的回调函数如下:

// ./packages/flutter/lib/src/rendering/binding.dart
void _handlePersistentFrameCallback(Duration timeStamp) {
  drawFrame();
  _mouseTracker.schedulePostFrameCheck();
}

void drawFrame() {
  pipelineOwner.flushLayout();
  pipelineOwner.flushCompositingBits();
  pipelineOwner.flushPaint();
  if (sendFramesToEngine) {
    renderView.compositeFrame(); // this sends the bits to the GPU
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
    _firstFrameSent = true;
  }
}

drawFrame方法主要是让渲染管线产生出一帧。

  • [6] scheduleFrame则是在需要的时候,通过调用WindowscheduleFrame方法安排一个新的帧。
// ./packages/flutter/lib/src/scheduler/binding.dart
void scheduleFrame() {
  ...
  ensureFrameCallbacksRegistered();
  window.scheduleFrame();
  _hasScheduledFrame = true;
}

这里的Window类是在引擎内实现的,通过Dart引擎扩展API,调用到C++的ScheduleFrame方法:

// (engine)./lib/ui/window/window.cc
void ScheduleFrame(Dart_NativeArguments args) {
  UIDartState::Current()->window()->client()->ScheduleFrame();
}

2. 渲染管线

下面看一下PipelineOwner,这个类是渲染树的持有者,维护布局、合成、绘制和可达性的状态。
PipelineOwner主要方法有以下几个

// ./packages/flutter/lib/src/rendering/object.dart
void flushLayout() { //[7]
  ...
  while (_nodesNeedingLayout.isNotEmpty) {
    final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
    _nodesNeedingLayout = <RenderObject>[];
    for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
      if (node._needsLayout && node.owner == this)
        node._layoutWithoutResize();
    }
  }
  ...
}

void flushCompositingBits() { // [8]
  ...
  _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
  for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) {
    if (node._needsCompositingBitsUpdate && node.owner == this)
      node._updateCompositingBits();
  }
  _nodesNeedingCompositingBitsUpdate.clear();
  ...
}

void flushPaint() { // [9]
  ...
  final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
  _nodesNeedingPaint = <RenderObject>[];
  // Sort the dirty nodes in reverse order (deepest first).
  for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
    if (node._needsPaint && node.owner == this) {
      if (node._layer.attached) {
        PaintingContext.repaintCompositedChild(node);
      } else {
        node._skippedPaintingOnLayer();
      }
    }
  }
  ...
}

void flushSemantics() { // [10]
  ...
  final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
    ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
  _nodesNeedingSemantics.clear();
  for (final RenderObject node in nodesToProcess) {
    if (node._needsSemanticsUpdate && node.owner == this)
      node._updateSemantics();
  }
  _semanticsOwner.sendSemanticsUpdate();
  ...
}
  • [7] flushLayout方法用于更新所有“脏”RenderObject的布局信息。该方法时渲染管线中的核心步骤之一。布局信息需要在绘制之前处理“干净”,这样RenderObject就可以在屏幕上出现在最新的位置。
  • [8] flushCompositingBits方法用于更新RenderObjectneedsCompositing字位。
  • [9] flushPaint方法用于更新所有RenderObject的显示列表,是渲染管线核心步骤之一。该步骤在布局操作之后,场景合成之前。
  • [10] flushSemantics方法更新RenderObject的语义。该方法也是核心步骤。

4. 总结

Framework的启动从Flutter App的main方法开始,通过runApp方法启动。

  1. 将Flutter App中的Widget树附着到WidgetsBinding上;
  2. 驱动渲染管线绘制首帧;
  3. 再需要的情况下,通过WindowscheduleFrame方法驱动引擎发起新的一帧。

你可能感兴趣的:(Flutter源码阅读分析)