当前位置: 首页 > article >正文

flutter 专题四十七 Flutter 应用启动流程分析

众所周知,任何应用程序的启动都是从main()函数开始的,Flutter也不例外,main.dart文件的main函数开始的,代码如下。

void main() => runApp(MyApp());

main函数则调用的是runApp函数,源码如下。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

上面代码中,用到了一个级联运算符(…),代表的含义是WidgetsFlutterBinding.ensureInitialized()生成的对象再调用scheduleAttachRootWidget和scheduleWarmUpFrame两个方法。先简单介绍下这三行代码的作用。

  1. 生成一个Flutter Engine(C++代码)和Flutter Framework(Dart代码)的中间桥接对象;
  2. 根据app生成一个渲染树;
  3. 绘制热身帧, 将渲染树生成的Layer图层通过Flutter Engine渲染到Flutter View上。

下面,我们分别对WidgetsFlutterBinding、scheduleAttachRootWidget和scheduleWarmUpFrame来分析Flutter的启动流程。

一、 WidgetsFlutterBinding

点击打开WidgetsFlutterBinding类,该类的代码如下所示。

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      //构造方法调用
      WidgetsFlutterBinding();
    //返回对象WidgetsBinding
    return WidgetsBinding.instance!;
  }
}

可以看到,WidgetsFlutterBinding继承自BindingBase,混入了GestureBinding,SchedulerBinding,ServicesBinding,PaintingBinding,SemanticsBinding,RendererBinding和WidgetsBinding7个mixin。

ensureInitialized方法就是获取WidgetsBinding.instance单例的过程。由于mixin没有构造方法,所以WidgetsFlutterBinding()实际调用的是父类BindingBase的构造方法,代码如下。

BindingBase() {
  // 调用initInstances
  initInstances();
}

WidgetsFlutterBinding混入的7个mixin都重写了initInstances()方法,所以他们各自的initInstances()都会被调用,调用的逻辑如下图所示。
在这里插入图片描述
通过使用mixin设计,实现了高内聚低耦合和模块职责单一,并且通过mixin依赖,实现了initInstances()方法调用的串行按执行顺序。

1.1 FlutterView

FlutterView是Flutter Engine给Flutter Framework开放的用户界面和事件的接口,可以把Flutter Framework理解为围绕FlutterView的一个处理框架。所以其重要性不言而喻。上面WidgetsFlutterBinding混入的多个mixin主要就是处理window对象(即FlutterView对象的)的回调事件和提交渲染内容,所以理解FlutterView是非常有必要的。事实上,window对象是BindingBase的一个变量。

<!-- BindingBase -->
ui.SingletonFlutterWindow get window => ui.window;

ui.window是PlatformDispatcher.instance中windowId为0的主window,如下所示。

<!-- window.dart -->
final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance);

SingletonFlutterWindow的继承关系如下。

abstract class FlutterView {}
class FlutterWindow extends FlutterView {}
class SingletonFlutterWindow extends FlutterWindow {}

FlutterView是一个抽象类,完整的代码如下。

abstract class FlutterView {

  PlatformDispatcher get platformDispatcher;
  ViewConfiguration get viewConfiguration;
  double get devicePixelRatio => viewConfiguration.devicePixelRatio;
  Rect get physicalGeometry => viewConfiguration.geometry;
  Size get physicalSize => viewConfiguration.geometry.size; 
  WindowPadding get viewInsets => viewConfiguration.viewInsets;
  WindowPadding get viewPadding => viewConfiguration.viewPadding;
  WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets;
  WindowPadding get padding => viewConfiguration.padding;
  void render(Scene scene) => _render(scene, this);
  void _render(Scene scene, FlutterView view) native 'PlatformConfiguration_render';
}

FlutterView有几个重要的属性和方法。

  1. PlatformDispatcher是FlutterView的核心,FlutterView是对它的一层封装,是真正向Flutter Engine发送消息和得到回调的类;
  2. ViewConfiguration是Platform View的一些信息的描述,其中主要包括几个信息。
  • devicePixelRatio:物理像素和虚拟像素的比值。这个和手机有关,譬如iPhone手机可能是2或者3,Android手机就有可能是个小数,譬如3.5等。
  • geometry:Flutter渲染的View在Native platform中的位置和大小。
  • viewInsets:各个边显示的内容和能显示内容的边距大小;譬如:没有键盘的时候viewInsets.bottom为0,当有键盘的时候键盘挡住了一些区域,键盘底下无法显示内容,所以viewInsets.bottom就变成了键盘的高度。
  • padding:系统UI的显示区域如状态栏,这部分区域最好不要显示内容,否则有可能被覆盖了。譬如,很多iPhone顶部的刘海区域,padding.top就是其高度。
  • viewPadding:viewInsets和padding的和。
FlutterWindow

FlutterWindow没有什么功能,只是封装了一个构造方法,接下来我们来看看SingletonFlutterWindow的一些重要代码。

onMetricsChanged
evicePixelRatio, physicalSize, padding和viewInsets等的变化会触发的回调onMetricsChanged()函数。

VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged;
set onMetricsChanged(VoidCallback? callback) {
    platformDispatcher.onMetricsChanged = callback;
}

手机设置的地区(如中国大陆),以及设置的地区更改后收到的回调onLocaleChanged,如下所示。

Locale get locale => platformDispatcher.locale;

VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged;
set onLocaleChanged(VoidCallback? callback) {
    platformDispatcher.onLocaleChanged = callback;
}  

文字缩放倍率变化后会回调onTextScaleFactorChanged。

  VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged;
  set onTextScaleFactorChanged(VoidCallback? callback) {
    platformDispatcher.onTextScaleFactorChanged = callback;
  }

除了上面的这几个外,其他的调用都会回调某个函数。而FlutterView对象window本质上是对PlatformDispatcher的封装,从PlatformDispatcher获取一些界面相关信息,获取从Flutter Engine 发送来的事件,然后触发和转发相应的回调方法。

1.2 BindingBase

BindingBase是一个抽象类,源码如下。

abstract class BindingBase {
    BindingBase() {
        // 初始化
        initInstances();
    }
    // 单例window
    ui.SingletonFlutterWindow get window => ui.window;
}

BindingBase主要有两个作用:

  • 构造函数调用initInstances方法,其实是为了依次调用7个mixin的initInstances方法。
  • 提供了一个window单例。
1.3 RendererBinding

RendererBinding的功能主要和渲染树相关,打开RendererBinding的源码,首先来看initInstances()初始化方法。

void initInstances() {
    super.initInstances();
    _instance = this;
    // 1
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    // 2
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    // 3
    initRenderView();
    
    _handleSemanticsEnabledChanged();
    // 4
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    // 5
    initMouseTracker();
}

在上面的代码中,我们来依次分析下都干了啥。

  1. 生成了一个PipelineOwner对象。它的主要作用是收集需要更新的RenderObjects,然后借助RendererBinding进行UI刷新。
  2. 处理window对象的onMetricsChanged、onTextScaleFactorChanged等回调方法。
  3. initRenderView生成了一个RenderView对象renderView,然后将renderView设置为_pipelineOwner的根节点rootNode。
  4. addPersistentFrameCallback调用的是SchedulerBinding的方法,PersistentFrameCallback主要执行的是Widget的build / layout / paint等一系列操作。
<!-- SchedulerBinding.dart -->
void addPersistentFrameCallback(FrameCallback callback) {
    _persistentCallbacks.add(callback);
}

5,生成一个MouseTracker对象,处理hitTestResult或者PointerAddedEvent和PointerRemovedEvent事件。

@visibleForTesting
  void initMouseTracker([MouseTracker tracker]) {
    _mouseTracker?.dispose();
    _mouseTracker = tracker ?? MouseTracker(pointerRouter, renderView.hitTestMouseTrackers);
  }
1.4 SemanticsBinding

Semantics主要就是描述应用程序中的UI信息,而在iOS和Android主要是用于读屏使用,帮助有视力障碍的人使用,SemanticsBinding的源码如下。

mixin SemanticsBinding on BindingBase {
    void initInstances() {
        super.initInstances();
        _instance = this;
        _accessibilityFeatures = window.accessibilityFeatures;
    }
}

1.5 PaintingBinding

PaintingBinding是一个处理图片缓存的mixin,下面我们来看看PaintingBinding的主要代码,首先看一下initInstances()初始化方法。

mixin PaintingBinding on BindingBase, ServicesBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();
    shaderWarmUp?.execute();
}
  • _imageCache是图片缓存的类,最大能存1000张图片,最大内存是100MB;
  • shaderWarmUp?.execute()是一个异步方法,初始化了一个默认的着色器,避免需要着色器的时候再初始化出现掉帧现象。

同时,createImageCache()方法内部还调用了handleMemoryPressure(),如下所示。

void handleMemoryPressure() {
    super.handleMemoryPressure();
    imageCache?.clear();
  }

之所以这么处理,是因为图片存储非常耗内存,所以当App内存警告时需要清除掉缓存。

1.6 ServicesBinding

ServicesBinding的主要功能是接收MethodChannel和SystemChannels传递过来的消息,下面来看看ServicesBinding的主要代码。首先,还是看看initInstances()的代码。

void initInstances() {
    super.initInstances();
    _instance = this;
    // 1
    _defaultBinaryMessenger = createBinaryMessenger();
    // 2
    _restorationManager = createRestorationManager();
    // 3
    window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    // 4
    initLicenses();
    // 5
    SystemChannels.system.setMessageHandler(handleSystemMessage);
}

下面,分别来看一下每一行代码的作用。
1,createBinaryMessenger()创建了一个MethodChannel;
2,createRestorationManager()创建了一个RestorationManager用于恢复界面数据的功能;
3,通过第一步创建的_defaultBinaryMessenger实现和Plugin插件的通信
4,initLicenses是给一些文件加上Licenses说明;
5,发送SystemChannels传递过来消息;

1.7 SchedulerBinding

SchedulerBinding主要处理任务调度,Flutter的调度分为idle、transientCallbacks、midFrameMicrotasks、persistentCallbacks和postFrameCallbacks几个阶段。

  • idle:此阶段没有绘制帧任务处理,主要处理Task,Microtask,Timer回调,用户输入和手势,以及其他一些任务。
  • transientCallbacks:这个阶段主要处理动画状态的计算和更新。
  • midFrameMicrotasks:这个阶段处理transientCallbacks阶段触发的Microtasks。
  • persistentCallbacks:这个阶段主要处理build/layout/paint等操作。
  • postFrameCallbacks:这个阶段主要在处理下一帧之前,做一些清理工作或者准备工作。

接下来,我们看一下handleAppLifecycleStateChanged()方法。

AppLifecycleState? get lifecycleState => _lifecycleState;
void handleAppLifecycleStateChanged(AppLifecycleState state) {
    assert(state != null);
    _lifecycleState = state;
    switch (state) {
      case AppLifecycleState.resumed:
      case AppLifecycleState.inactive:
        _setFramesEnabledState(true);
        break;
      case AppLifecycleState.paused:
      case AppLifecycleState.detached:
        _setFramesEnabledState(false);
        break;
    }
}

void _setFramesEnabledState(bool enabled) {
    if (_framesEnabled == enabled)
      return;
    _framesEnabled = enabled;
    if (enabled)
      scheduleFrame();
}

上面代码的主要作用是监听生命周期变化,生命周期的状态改变设置_framesEnabled的值,如果_framesEnabled为false停止刷新界面;如果_framesEnabled为true调用scheduleFrame向Native Platform请求刷新视图的请求。接下来,再看一下scheduleFrame()方法。

void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled)
      return;
    // 1  
    ensureFrameCallbacksRegistered();
    // 2
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }


void ensureFrameCallbacksRegistered() {
    window.onBeginFrame ??= _handleBeginFrame;
    window.onDrawFrame ??= _handleDrawFrame;
}

其中,ensureFrameCallbacksRegistered()是先确保向window注册了onBeginFrame和 onDrawFrame两个重要回调函数。而window.scheduleFrame()是向Native platform发起一个刷新视图的请求;发送这个请求后,Native platform会在合适的时间调用onBegineFrame和onDrawFrame这两个函数, 这两个回调会完成刷新视图所需的操作,比如更新widgets、动画、和完成渲染等。这些都完成后再调用window.scheduleFrame(),一直循环下去,直到程序退出前台或者程序退出。

接下来看一下handleBeginFrame()方法的源码。

void handleBeginFrame(Duration? rawTimeStamp) {
    _hasScheduledFrame = false;
    try {
      _schedulerPhase = SchedulerPhase.transientCallbacks;
      final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
      _transientCallbacks = <int, _FrameCallbackEntry>{};
      callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
        if (!_removedIds.contains(id))
          _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
      });
      _removedIds.clear();
    } finally {
      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
    }
}

handleBeginFrame的功能是执行_transientCallbacks中的所有函数。向transientCallbacks中添加回调主要是Ticker.scheduleTick方法,是动画框架的一部分。

接着,再看一下handleDrawFrame()方法的代码。

void handleDrawFrame() {

    try {
      // 1
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (final FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
      // 2
      _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;
      _currentFrameTimeStamp = null;
    }
}

final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];
final List<FrameCallback> _postFrameCallbacks = <FrameCallback>[];  

handleDrawFrame中执行了两种回调函数,persistentCallbacks和 postFrameCallbacks中所有的回调函数。

1.8GestureBinding

GestureBinding主要处理用户的各种手势事件,初始化方法如下。

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
    void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
  }
}

GestureBinding用_handlePointerDataPacket来处理window的onPointerDataPacket方法,_handlePointerDataPacket的源码如下。

void _handlePointerDataPacket(ui.PointerDataPacket packet) {
    _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
    if (!locked)
      _flushPointerEventQueue();
}

void _flushPointerEventQueue() {
    while (_pendingPointerEvents.isNotEmpty)
      handlePointerEvent(_pendingPointerEvents.removeFirst());
}

void handlePointerEvent(PointerEvent event) {
    _handlePointerEventImmediately(event);
}

void _handlePointerEventImmediately(PointerEvent event) {
    HitTestResult? hitTestResult;
    if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
      // 1
      hitTestResult = HitTestResult();
      // 2
      hitTest(hitTestResult, event.position);
      // 3
      if (event is PointerDownEvent) {
        _hitTests[event.pointer] = hitTestResult;
      }
    } else if (event is PointerUpEvent || event is PointerCancelEvent) {
      // 4
      hitTestResult = _hitTests.remove(event.pointer);
    } else if (event.down) {
      hitTestResult = _hitTests[event.pointer];
    }
    if (hitTestResult != null ||
        event is PointerAddedEvent ||
        event is PointerRemovedEvent) {
      // 5    
      dispatchEvent(event, hitTestResult);
    }
}

_handlePointerDataPacket通过一系列的方法调用,最后调用_handlePointerEventImmediately方法。当event是PointerDownEvent或者PointerHoverEvent时,新建一个HitTestResult对象,它有一个path属性,用来记录事件传递所经过的的节点。在这里插入代码片HitTestResult把GestureBinding也加在了path中,相关代码如下。

void hitTest(HitTestResult result, Offset position) {
  result.add(HitTestEntry(this));
}

如果event是PointerDownEvent,将这个event加入到_hitTests中, 为了在event.down-即移动的时候也能获取到它。

final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};

当event是PointerUpEvent或者PointerCancelEvent时,将这个event从_hitTests中移除。最后,调用dispatchEvent(event, hitTestResult)方法。如果您有印象,RendererBinding中我们提到过dispatchEvent方法。

<!-- rendererBinding.dart -->
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
    _mouseTracker!.updateWithEvent(event,
          () => hitTestResult ?? renderView.hitTestMouseTrackers(event.position));
    super.dispatchEvent(event, hitTestResult);
}    

其中,最重要的调用逻辑renderView.hitTestMouseTrackers(event.position)),会从renderview一直遍历它的child,将沿途的Widget加入到path中,涉及的代码如下。

<!-- view.dart -->
HitTestResult hitTestMouseTrackers(Offset position) {
    final BoxHitTestResult result = BoxHitTestResult();
    hitTest(result, position: position);
    return result;
}

bool hitTest(HitTestResult result, { required Offset position }) {
    if (child != null)
      child!.hitTest(BoxHitTestResult.wrap(result), position: position);
    result.add(HitTestEntry(this));
    return true;
}

<!-- box.dart -->
bool hitTest(BoxHitTestResult result, { required Offset position }) {
    if (_size!.contains(position)) {
      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
        result.add(BoxHitTestEntry(this, position));
        return true;
      }
    }
    return false;
  }

当遍历完renderView的所有widget后,将hitTestResult返回给GestureBinding的dispatchEvent方法,然后遍历path数组,逐个调用handleEvent方法处理相关的事件,这和原生的触摸事件的处理流程是一致的。

<!-- gestureBinding.dart -->
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
    for (final HitTestEntry entry in hitTestResult.path) {
      entry.target.handleEvent(event.transformed(entry.transform), entry);
    }
}

void handleEvent(PointerEvent event, HitTestEntry entry) {
    pointerRouter.route(event);
    if (event is PointerDownEvent) {
      gestureArena.close(event.pointer);
    } else if (event is PointerUpEvent) {
      gestureArena.sweep(event.pointer);
    } else if (event is PointerSignalEvent) {
      pointerSignalResolver.resolve(event);
    }
}
1.9 WidgetsBinding

WidgetsBinding主要处理widget tree的一些逻辑,初始化方法如下。

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

    // 1
    _buildOwner = BuildOwner();
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    // 2
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
}

上面代码的主要作用是初始化了一个BuildOwner对象,执行widget tree的build任务,然后再
执行了一些window的回调。至此,第一步WidgetsFlutterBinding.ensureInitialized()所涉及的知识点就介绍完了。

二、scheduleAttachRootWidget

scheduleAttachRootWidget的主要作用就是和根视图进行绑定。首先,来看一下scheduleAttachRootWidget的源码。

void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
}

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

scheduleAttachRootWidget异步调用了attachRootWidget方法。attachRootWidget中初始化了一个RenderObjectToWidgetAdapter对象,构造函数传入了renderView和rootWidget。renderView就是RendererBinding的initInstances方法中初始化的那个对象,rootWidget则是MyApp(),即我们看到的界面。

从构造函数的参数名我们可以看到,renderView是容器,rootWidget是这个容器的child。也就是说renderView是所有的Widget的根。

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
  RenderObjectToWidgetAdapter({
    this.child,
    required this.container,
    this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));

RenderObjectToWidgetAdapter对象调用attachToRenderTree方法,把构造的工具_buildOwner传进去。attachToRenderTree的源码如下。

RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
    if (element == null) {
      owner.lockState(() {
        // 1
        element = createElement();
        element!.assignOwner(owner);
      });
      owner.buildScope(element!, () {
        // 2
        element!.mount(null, null);
      });
      // 3
      SchedulerBinding.instance!.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
}

创建了一个RenderObjectElement的子类RenderObjectToWidgetElement,并将构造工具buildOwner引用给了它。然后,element调用mount方法。下面,我们来看一下RenderObjectToWidgetElement的mount方法。

// RenderObjectToWidgetElement
void mount(Element? parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _rebuild();
}

// RenderObjectElement
void mount(Element? parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _renderObject = widget.createRenderObject(this);
    attachRenderObject(newSlot);
    _dirty = false;
}

// Element 
void mount(Element? parent, dynamic newSlot) {
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null)
      _owner = parent.owner;
    final Key? key = widget.key;
    if (key is GlobalKey) {
      key._register(this);
    }
    _updateInheritance();
  }

RenderObjectToWidgetElement的mount方法先调用Element的mount方法。主要的作用就是设置_parent,_slot,_owner,_depth等的值;

然后,调用RenderObjectElement的mount方法。创建了一个renderObject,其实就是renderView。然后把这个renderObject挂载到RenderObject Tree上,之前的RenderObject Tree没有内容,所以renderView就是根节点。

接下来,再看一下RenderObjectToWidgetElement的_rebuild方法。

void _rebuild() {
    try {
      _child = updateChild(_child, widget.child, _rootChildSlot);
    } catch (exception, stack) {
    }
}

_rebuild的功能就是Build子Widget,代码如下。

Element? updateChild(Element? child, Widget? newWidget, dynamic newSlot) {
    final Element newChild;
    if (child != null) {
      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 {
      // 创建Element
      newChild = inflateWidget(newWidget, newSlot);
    }
    return newChild;
  }

updateChild中如果child为null,newWidget不为null, 则会调用newChild = inflateWidget(newWidget, newSlot);

Element inflateWidget(Widget newWidget, dynamic newSlot) {
    final Key? key = newWidget.key;
    final Element newChild = newWidget.createElement();
    newChild.mount(this, newSlot);
    return newChild;
  }

inflateWidget先创建一个Element,然后这个Element调用mount方法。事实上,inflateWidget的主要作用就是使用buildOwner对 Widget 树—renderview->MyApp->MaterialApp… 一直Build下去,直到遍历完成。

三、scheduleWarmUpFrame

scheduleWarmUpFrame是SchedulerBinding的一个方法,如下所示。

void scheduleWarmUpFrame() {
    Timer.run(() {
      handleBeginFrame(null);
    });
    Timer.run(() {
      handleDrawFrame();
      if (hadScheduledFrame)
        scheduleFrame();
    });

    lockEvents(() async {
      await endOfFrame;
    });
}

scheduleWarmUpFrame就是调用handleBeginFrame和handleDrawFrame方法绘制一帧呈递给GPU去显示。这里需要说明的是,scheduleWarmUpFrame是立即去绘制的,没有等待Vsyn的通知,因为启动的显示要越快越好。后面的lockEvents也是为了等待预约帧绘制完成后再去执行其他的任务。


http://www.kler.cn/a/533955.html

相关文章:

  • 【漫话机器学习系列】070.汉明损失(Hamming Loss)
  • 数据库开发常识(10.6)——SQL性能判断标准及索引误区(1)
  • w185客户关系管理系统
  • swagger使用指引
  • Spring Boot 2 快速教程:WebFlux处理流程(五)
  • Kamailio 不通过 dmq 实现注册复制功能
  • 【力扣】240.搜索二维矩阵 II
  • 4 前端前置技术(上):AJAX技术、Axios技术(前端发送请求)
  • idea分析sql性能
  • win32汇编环境,对话框程序中自定义工具栏的使用示例
  • neo4j-在Linux中安装neo4j
  • 【华为OD-E卷 - 115 数组组成的最小数字 100分(python、java、c++、js、c)】
  • C# Action和 Func的用法
  • [操作系统] 进程终止
  • 大模型 Llama 微调如何适配中文_词表扩展
  • 如何开发一个大语言模型,开发流程及需要的专业知识
  • 【数学】矩阵、向量(内含矩阵乘法C++)
  • Ubuntu22.04如何设置linux-lowlatency核心
  • 扩增子分析|零模型2——基于βNTI的微生物随机性和确定性装配过程(箱线图和柱状图R中实现)
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_num 函数
  • 2024年Web前端最新Java进阶(五十五)-Java Lambda表达式入门_eclipse lambda(1),面试必备
  • 高压GaN(氮化镓)器件在工业和汽车应用存在的致命弱点
  • git 设置分支跟踪
  • Nginx通过设置自定义标记识别代理调用
  • VMware Win10下载安装教程(超详细)
  • 《手札·开源篇》基于开源Odoo软件与Deepseek的智能企业管理系统集成方案