阅读 86

Flutter框架层启动源码剖析

博主相关文章列表

Flutter 框架实现原理

Flutter 框架层启动源码剖析

Flutter 页面更新流程剖析

Flutter 事件处理源码剖析

Flutter 路由源码剖析

Flutter 安卓平台源码剖析

Flutter 自定义控件之RenderObject

Flutter框架层启动源码剖析

PS: 剖析源码版本为Flutter 1.18

入口函数,其主要作用是注入给定的小控件并将其附加到屏幕上。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}
复制代码
  1. 初始化一个WidgetsBinding的全局单例
  2. 创建根widget并添加到RenderView上,在这个过程中完成Element树和RenderObject树的生成
  3. 执行渲染

WidgetsBinding 初始化

static WidgetsBinding ensureInitialized() {
  if (WidgetsBinding.instance == null)
    WidgetsFlutterBinding();
  return WidgetsBinding.instance;
}
复制代码

这里通过子类WidgetsFlutterBinding示例化了一个WidgetsBinding对象。但是这里WidgetsFlutterBinding没有显式声明构造方法,因此我们查看它的父类的构造方法实现

// [..\flutter\packages\flutter\lib\src\foundation\binding.dart]

BindingBase() {
  // 删除断言
  developer.Timeline.startSync('Framework initialization');
  initInstances();
  initServiceExtensions();
  developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
  developer.Timeline.finishSync();
}
复制代码

可以看到,这里面主要调用了initInstances()做了一些初始化操作,但是基类BindingBase自己的initInstances()是一个空的实现,因此要查看其他父类中的实现。这里我们需要注意到WidgetsFlutterBinding类的继承情况

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

   // ......
}
复制代码

由此,这里initInstances()方法会调用最外侧的WidgetsBinding中的具体实现。检查代码可知,混入的每一个类中都实现了initInstances方法,并且还调用了super.initInstances(),这样一来,就会从最外层的WidgetsBinding开始,依次链式调用每一个混入类中的initInstances()方法,完成各个Binding的初始化。

接下来我们先进入WidgetsBinding中的initInstances实现,逻辑不多,主要是创建了BuildOwner对象,并给window设置了一些回调函数

/// [WidgetsBinding]

void initInstances() {
  super.initInstances();
  _instance = this;

  _buildOwner = BuildOwner();
  buildOwner.onBuildScheduled = _handleBuildScheduled;
  window.onLocaleChanged = handleLocaleChanged;
  window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
  SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
  FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
复制代码

再查看RendererBinding中的initInstances实现,这里创建了PipelineOwner对象,也给window设置了另一些回调函数

/// [RendererBinding]

void initInstances() {
  super.initInstances();
  _instance = this;
  _pipelineOwner = PipelineOwner(
    onNeedVisualUpdate: ensureVisualUpdate,
    onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
    onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
  );
  window
    ..onMetricsChanged = handleMetricsChanged
    ..onTextScaleFactorChanged = handleTextScaleFactorChanged
    ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
    ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
    ..onSemanticsAction = _handleSemanticsAction;
  // 创建一个根RenderObject
  initRenderView();
  _handleSemanticsEnabledChanged();
  assert(renderView != null);
  // 注册persistentCallbacks回调
  addPersistentFrameCallback(_handlePersistentFrameCallback);
  initMouseTracker();
}
复制代码

这里我们可以查看一下根RenderObject的创建

void initRenderView() {
  assert(renderView == null);
  renderView = RenderView(configuration: createViewConfiguration(), window: window);
  renderView.prepareInitialFrame();
}
复制代码

这里其他Binding的初始化先略过,我们查看一下出现多次的window是什么东西。根据官方的解释,Window是Flutter 框架连接宿主操作系统的接口

class Window {
  // 当前设备的DPI,即一个逻辑像素显示多少物理像素,数字越大,显示效果就越精细保真。
  // DPI是设备屏幕的固件属性,如Nexus 6的屏幕DPI为3.5 
  double get devicePixelRatio => _devicePixelRatio;

  // Flutter UI绘制区域的大小
  Size get physicalSize => _physicalSize;

  // 当前系统默认的语言Locale
  Locale get locale;

  // 当前系统字体缩放比例。  
  double get textScaleFactor => _textScaleFactor;  

  // 当绘制区域大小改变回调
  VoidCallback get onMetricsChanged => _onMetricsChanged;  
  // Locale发生变化回调
  VoidCallback get onLocaleChanged => _onLocaleChanged;
  // 系统字体缩放变化回调
  VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
  // 绘制前回调,一般会受显示器的垂直同步信号VSync驱动,当屏幕刷新时就会被调用
  FrameCallback get onBeginFrame => _onBeginFrame;
  // 绘制回调  
  VoidCallback get onDrawFrame => _onDrawFrame;
  // 点击或指针事件回调
  PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
  // 调度Frame,该方法执行后,onBeginFrame和onDrawFrame将紧接着会在合适时机被调用,
  // 此方法会直接调用Flutter engine的Window_scheduleFrame方法
  void scheduleFrame() native 'Window_scheduleFrame';
  // 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法
  void render(Scene scene) native 'Window_render';

  // 发送平台消息
  void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) ;
  // 平台通道消息处理回调  
  PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;

  ... //其它属性及回调
}
复制代码

我们可以发现,混入的那些Binding基本都是监听并处理Window对象的一些事件,然后将这些事件根据Flutter框架层的模型进行包装、抽象最后分发。

我们查看官方 WidgetsFlutterBinding文档可知,WidgetsFlutterBinding是将Framework与Flutter引擎绑定的胶水。BindingBase相当于所有Binding的基类,定义了一些共同行为。

  • GestureBinding: 绑定Framework手势子系统,是Framework事件模型与底层事件的绑定入口
  • ServicesBinding: 用于绑定平台消息通道(message channel),主要处理原生和Flutter通信
  • SchedulerBinding:监听刷新事件,绑定Framework绘制调度子系统
  • PaintingBinding: 绑定绘制库,主要用于处理图片缓存。
  • SemanticsBinding:语义化层与Flutter 引擎的桥梁,主要是辅助功能的底层支持
  • RendererBinding: 是渲染树与Flutter 引擎的桥梁
  • WidgetsBinding: 它是Flutter Widget层与引擎的桥梁

详细描述参见官方文档:

构建Element和RenderObject树

再看scheduleAttachRootWidget方法,它实际上调用了如下方法,它完成了 WidgetRenderObjectElement 三者的关联。具体代码在attachToRenderTree方法中

/// [WidgetsBinding]

void attachRootWidget(Widget rootWidget) {
  _readyToProduceFrames = true;
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
复制代码

BuildOwnerwidget框架的管理器类。该类跟踪哪些widgets需要重建,并处理其他适用于widgets树的任务,如管理树的非活动元素列表,并在调试时的热重载期间在必要时触发 "reassemble "命令。

/// [RenderObjectToWidgetAdapter]

RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
  /// 首次进来,element为空,因此创建根element
  if (element == null) {
    owner.lockState(() {
      element = createElement();
      assert(element != null);
      element.assignOwner(owner);
    });
    owner.buildScope(element, () {
      element.mount(null, null);
    });
    // This is most likely the first time the framework is ready to produce
    // a frame. Ensure that we are asked for one.
    SchedulerBinding.instance.ensureVisualUpdate();
  } else {
    element._newWidget = this;
    element.markNeedsBuild();
  }
  return element;
}
复制代码

首次执行时elementnull,所以执行createElement方法创建Element,而真正执行构建树的操作是 owner.buildScope方法。这个方法首先执行传入的回调,即执行element.mount(null, null)方法

/// [RenderObjectToWidgetElement]

@override
void mount(Element parent, dynamic newSlot) {
  assert(parent == null);
  super.mount(parent, newSlot);
  _rebuild();
}
复制代码

mount方法会首先调用父类的mount方法,即调用到RenderObjectElement类中的mount方法如下,在此处创建RenderObject对象,同时调用attachRenderObject方法生成RenderObject

/// [RenderObjectElement]

void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
   /// 代码已删除断言
  _renderObject = widget.createRenderObject(this);

  attachRenderObject(newSlot);
  _dirty = false;
}
复制代码

再看_rebuild方法,其中主要调用了updateChild方法,updateChild方法的主要作用是用给定的新配置(widget)更新给定的子元素。这里可以关注catch中的代码,正是此处生成了Flutter中常见的红屏报错页面ErrorWidget

/// [RenderObjectToWidgetElement] 

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: ErrorDescription('attaching to the render tree'),
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
      _child = updateChild(null, error, _rootChildSlot);
    }
  }
复制代码

updateChild方法移除注释与断言后如下,newWidgetchild的值存在几种不同的组合情况,详见相关类分析篇。当首次进入时,会执行inflateWidget,为根Widget的子Widget创建一个新的Element

/// [Element] 

Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  if (newWidget == null) {
    if (child != null)
      deactivateChild(child);
    return null;
  }
  Element newChild;
  if (child != null) {
    bool hasSameSuperclass = true;

    if (hasSameSuperclass && child.widget == newWidget) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      newChild = child;
    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      child.update(newWidget);

      newChild = child;
    } else {
      deactivateChild(child);
      newChild = inflateWidget(newWidget, newSlot);
    }
  } else {
    newChild = inflateWidget(newWidget, newSlot);
  }

  return newChild;
}
复制代码

inflateWidget方法删除断言后如下,可以看到,这里创建newChild后又调用了mount方法,依次递归遍历Widget树的子节点,不断为Widget创建对应的ElementRenderObject,以此完成“三颗树”的构建与关联。

/// [Element] 

Element inflateWidget(Widget newWidget, dynamic newSlot) {
  final Key key = newWidget.key;
  if (key is GlobalKey) {
    final Element newChild = _retakeInactiveElement(key, newWidget);
    if (newChild != null) {
      newChild._activateWithParent(this, newSlot);
      final Element updatedChild = updateChild(newChild, newWidget, newSlot);
      return updatedChild;
    }
  }
  final Element newChild = newWidget.createElement();
  newChild.mount(this, newSlot);
  return newChild;
}
复制代码

执行渲染

接下来调用scheduleWarmUpFrame进行了第一次绘制,具体实现在SchedulerBinding中。该方法会锁定事件调度直到完成为止。即在这次绘制完成之前都不会接收event(触摸事件等)。

该方法中主要调用了handleBeginFrame()handleDrawFrame()

/// [SchedulerBinding] 

void scheduleWarmUpFrame() {
  if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
    return;

  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  // 在这里使用计时器来确保microtasks在两者之间刷新
  Timer.run(() {
    assert(_warmUpFrame);
    handleBeginFrame(null);
  });
  Timer.run(() {
    assert(_warmUpFrame);
    handleDrawFrame();

    resetEpoch();
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });

  // 锁定事件,使触摸事件等在预定帧结束前不会自行插入。
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}
复制代码

其中handleBeginFrame方法用于渲染前的一些准备,主要是处理transientCallbacks回调,也就是触发动画相关的 Ticker 回调。而真正处理渲染的是handleDrawFrame方法,它由引擎调用以生成新帧。

/// [SchedulerBinding] 

void handleDrawFrame() {
  assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
  Timeline.finishSync(); // end the "Animate" phase
  try {
    // 处理persistentCallbacks回调
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (final FrameCallback callback in _persistentCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp);

    // 处理postFrameCallbacks回调
    _schedulerPhase = SchedulerPhase.postFrameCallbacks;
    final List<FrameCallback> localPostFrameCallbacks =
        List<FrameCallback>.from(_postFrameCallbacks);
    _postFrameCallbacks.clear();
    for (final FrameCallback callback in localPostFrameCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp);
  } finally {
    _schedulerPhase = SchedulerPhase.idle;
    Timeline.finishSync(); // end the Frame
    _currentFrameTimeStamp = null;
  }
}
复制代码

根据官方文档的解释,有以下三个回调队列(参见SchedulerBinding文档

  • transientCallbacks:由系统的Window.onBeginFrame 回调触发,一般用于处理动画的回调。
  • persistentCallbacks:由系统的Window.onDrawFrame 回调触发。永久callback,一经注册无法移除,由WidgetsBinding.instance.addPersitentFrameCallback()注册,主要处理布局与绘制工作。
  • postFrameCallbacks:在persistentCallbacks回调之后,Window.onDrawFrame 返回之前执行。它只会调用一次,调用后就会被系统移除。可由WidgetsBinding.instance.addPostFrameCallback()注册,通常用于State的更新。

可以看到,这里的代码逻辑主要就是处理persistentCallbackspostFrameCallbacks的回调,而框架正好又在RendererBinding初始化时注册了一个persistentCallbacks回调

/// [..\flutter\packages\flutter\lib\src\rendering\binding.dart]
/// [RendererBinding]

void initInstances() {
  super.initInstances();
  _instance = this;
 
  /// 省略部分代码
  addPersistentFrameCallback(_handlePersistentFrameCallback);
  /// .......
}

void _handlePersistentFrameCallback(Duration timeStamp) {
    // 注意,这里不是调用本类中的方法,而是依据WidgetsFlutterBinding混入的顺序调用
    // 实际上是调用的WidgetsBinding的drawFrame方法
    drawFrame();
    _mouseTracker.schedulePostFrameCheck();
}

void drawFrame() {
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    pipelineOwner.flushPaint();
    if (sendFramesToEngine) {
        renderView.compositeFrame(); // 这会将二进制数据发送到GPU
        pipelineOwner.flushSemantics(); // 将语义发送给操作系统
        _firstFrameSent = true;
    }
}
复制代码

以上代码中需要特别注意,回调中并不是直接调用RendererBinding中的drawFrame()方法,而是依据WidgetsFlutterBinding混入的顺序调用

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {}
复制代码

根据Dart mixin语法,混入多个类后,调用同名方法时,是从最外层开始调用。可以看到,这里是调用的WidgetsBinding.drawFrame()

/// [WidgetsBinding]


/// 抽取构建和渲染管道以生成一帧。
/// 这个方法被handleDrawFrame调用,当需要布局和绘制一帧时,引擎会自动调用这个方法。
void drawFrame() {
  TimingsCallback firstFrameCallback;
  if (_needToReportFirstFrame) {
    firstFrameCallback = (List<FrameTiming> timings) {

      if (!kReleaseMode) {
        developer.Timeline.instantSync('Rasterized first useful frame');
        developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
      }
      SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
      firstFrameCallback = null;
      _firstFrameCompleter.complete();
    };
   // 只有在调用[Window.render]时才会调用回调。
   // 当[sendFramesToEngine]在帧中被设置为false时,它将不会被调用,我们需要删除回调
    SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
  }

  try {
    if (renderViewElement != null)
      buildOwner.buildScope(renderViewElement);
    super.drawFrame();
    buildOwner.finalizeTree();
  } finally {

  }
  if (!kReleaseMode) {
    if (_needToReportFirstFrame && sendFramesToEngine) {
      developer.Timeline.instantSync('Widgets built first useful frame');
    }
  }
  _needToReportFirstFrame = false;
  if (firstFrameCallback != null && !sendFramesToEngine) {
    // 这个帧是延时的,并不是第一个发送给引擎的应该报告的帧
    _needToReportFirstFrame = true;
    SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
  }
}
复制代码

实际上,这里的主要逻辑是try中的代码

try {
    if (renderViewElement != null)
      // 将被标记为dirty的Element进行rebuild()
      buildOwner.buildScope(renderViewElement);
    // 调用父类的drawFrame,这里实际上调用的是RendererBinding中的drawFrame()方法
    super.drawFrame();
    // 通过卸载任何不再活动的元素来完成元素构建过程
    buildOwner.finalizeTree();
  }
复制代码

其中通过调用super.drawFrame()又再次回到RendererBinding中的drawFrame()方法

// 更新需要计算布局的渲染对象。在这个阶段,计算每个渲染对象的大小和位置。
pipelineOwner.flushLayout();
// 更新具有dirty compositing bits的所有渲染对象。 在此阶段,每个渲染对象将了解其子对象是否需要合成。 
// 在选择如何实现视觉效果(例如剪裁)时,在绘画阶段将使用此信息。
pipelineOwner.flushCompositingBits();
// 访问需要绘制的渲染对象进行绘制
pipelineOwner.flushPaint();
// 该方法将画好的layer传给引擎,该方法调用结束后,屏幕就会显示内容
renderView.compositeFrame(); 
// 如果启用了语义,则该方法将编译渲染对象的语义,传给系统用于辅助功能,如搜索等。
pipelineOwner.flushSemantics(); 
复制代码

小结

依据官方的drawFrame 文档的描述,绘制过程经历以下十个阶段:

1、动画阶段:handleBeginFrame方法是用Window.onBeginFrame注册的, 按注册顺序调用所有用 scheduleFrameCallback注册的transientCallbacks。其中包括所有驱动AnimationController对象的Ticker实例,也就是说此时所有的活动的Animation对象的tick 在此刻回调。

2、Microtask:在handleBeginFrame返回后,任何被transientCallbacks安排的微任务都会被运行。这通常包括来自TickerAnimationController的完成该帧的Future 回调。

handleBeginFrame之后,调用了Window.onDrawFrame注册的handleDrawFrame,它调用了所有的persistentCallbacks,其中最值得注意的是drawFrame这个方法,其过程如下。

3、构建阶段: 重建widget树中所有的dirty 元素(见 State.build). 请参阅 State.setState了解更多关于标记一个widget dirty的构建的细节。有关此步骤的更多信息,请参见 BuildOwner

4、布局阶段: 系统中的所有标记 dirty的 RenderObject 都会被布局(参见 RenderObject.performLayout)。请参阅RenderObject.markNeedsLayout,了解关于标记一个对象脏以进行布局的更多细节。

5、合成位阶段:更新任何标记dirty的 RenderObject对象上的compositing bits 。请参见 RenderObject.markNeedsCompositingBitsUpdate

6、绘制阶段:系统中所有标记dirty 的RenderObject都会被重新绘制(见 RenderObject.paint)。这将生成Layer树。请参阅 RenderObject.markNeedsPaint,以获取有关将对象标记为dirty 的更多详细信息。

7、合成阶段:图层树被转化为一个Scene 并发送给GPU。

8、语义阶段:系统中所有标记 dirty的 RenderObject的语义都会被更新(见RenderObject.assembleSemanticsNode)。这将生成 SemanticsNode树。请参阅 RenderObject.markNeedsSemanticsUpdate以了解关于标记一个对象为dirty的语义的细节。

有关步骤4-8的更多详细信息,请参见PipelineOwner

9、Widget层的完成阶段:Widget树已完成。这将导致在从Widget树中删除的任何对象上调用State.dispose。有关更多详细信息,请参见 BuildOwner.finalizeTree

10、调度层的最终化阶段。在drawFrame返回后,handleDrawFrame会调用postFrameCallbacks回调(用addPostFrameCallback注册的)。

总结

框架主要构建的三课树的根对象分别如下

根Widget:由我们自己创建,通过runApp函数传入

void main() {
  runApp(MyApp());
}
复制代码

根Element:为RenderObjectToWidgetElement类型,通过调用RenderObjectToWidgetAdaptercreateElement创建

@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
复制代码

根RenderObject:为RenderView类型。注意,由RenderObjectToWidgetAdaptercreateRenderObject方法获取而非创建。

@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
复制代码

这里RenderObjectToWidgetAdapter中的container为外部传入的renderView

核心调用的时序图如下

视频课程

本篇博客视频内容可访问B站链接 框架启动源码剖析 您觉得有帮助,别忘了点赞哦

如需要获取完整的Flutter全栈式开发课程,请访问以下地址 Flutter全栈式开发之Dart 编程指南 二维码

Flutter 全栈式开发指南