从Flutter的Binding们说起

关于Flutter系列,本打算从main函数一路看下去,然鹅一进来就看到了这么一个类

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

这都是啥?仔细数了下不多不少7个 依次看一下

BindingBase

先看一下这个类的注释(这里我努力翻译了下,可惜自身水平实在低,感觉还不如机翻,有兴趣的老哥可以自己去看一下英语注释 TT 难为大家了

/// 提供单利服务的mixin们的基类
///
/// 使用on继承此类并实现initInstances方法 这个mixin在app的生命周期内只能被构建一次,在checked
/// 模式下会对此断言
///
/// 用于编写应用程序的最顶层将具有一个继承自[BindingBase]并使用所有各种[BindingBase] 
/// mixins(例如[ServicesBinding])的具体类。
/// 比如Flutter中的Widgets库引入了一个名为[WidgetsFlutterBinding]的binding,定义了如何绑定
/// 可以隐性(例如,[WidgetsFlutterBinding]从[runApp]启动),或者需要应用程序显式调用构造函数

实际就是用来绑定flutter引擎,我们看一下代码(因为本文只为了简单了解过程,所以会省略一些断言或者其他代码~

abstract class BindingBase {
  BindingBase() {
    initInstances();//在构造函数里进行初始化
    initServiceExtensions();//初始化扩展服务
  }

  ui.Window get window => ui.window;//提供window
  
  @protected
  @mustCallSuper
  void initInstances() {//初始化,其他binding mixin可以重写此类
  }

  @protected
  @mustCallSuper
  void initServiceExtensions() {}//用于子类重写该方法,用于注册一些扩展服务。

  @protected
  bool get locked => _lockCount > 0;
  int _lockCount = 0;

  @protected
  Future<void> lockEvents(Future<void> callback()) {
    _lockCount += 1;
    final Future<void> future = callback();
    future.whenComplete(() {
      _lockCount -= 1;
      if (!locked) {
        unlocked();
      }
    });
    return future;
  }

  @protected
  @mustCallSuper
  void unlocked() {//解锁
    assert(!locked);
  }

  Future<void> reassembleApplication() {//代码更改时用于重绘页面
    return lockEvents(performReassemble);
  }

  @mustCallSuper
  @protected
  Future<void> performReassemble() {//重绘方法,需要重写
    return Future<void>.value();
  }
    ···省略一些服务扩展方法
}

好,基类就先看到这里,接下来我们按照顺序看看其他类

GestureBinding

看名字就知道,手势相关 直接看代码,类注释也说明了,用来绑定手势系统

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {//关于继承的命中测试相关类,我们暂时不去关注
  @override
  void initInstances() {//初始化,关联window
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;//给window添加事件回调
  }

  @override
  void unlocked() {
    super.unlocked();
    _flushPointerEventQueue();//刷新事件队列
  }

  static GestureBinding get instance => _instance;
  static GestureBinding _instance;//单例 因为binding都是单例,以后的代码就不展示了

  final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>();//事件队列

  void _handlePointerDataPacket(ui.PointerDataPacket packet) {//处理事件
    _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));//先放入事件队列
    if (!locked)
      _flushPointerEventQueue();//刷新缓存的事件队列
  }

  void _flushPointerEventQueue() {
    assert(!locked);
    while (_pendingPointerEvents.isNotEmpty)//依次处理缓存的事件
      _handlePointerEvent(_pendingPointerEvents.removeFirst());
  }

  //具体的分发逻辑我们暂时不分析了
  @override // from HitTestDispatcher
  void dispatchEvent(PointerEvent event, HitTestResult hitTestResult) {
    //分发事件
  }
}

下面看第二个

ServicesBinding

监听平台消息并将它们定向到[defaultBinaryMessenger]。
同时也提供了LICENSE文件的缓存等功能(其实讲道理我不知道这个LICENSE是啥,有老哥说明一下么TT

接下来

SchedulerBinding

看名字就知道,和任务调度相关 先看一下它的注释

/// 用于调度以下内容的运行:
///
/// * _临时回调_,由系统的[Window.onBeginFrame]触发回调,用于将应用程序的行为同步到系统
///  显示 例如,[Ticker]和[AnimationController]的触发器。
///
/// * _Persistent callbacks_,(持续回掉)由系统的[Window.onDrawFrame]触发回调,用于在临
///  时回掉后执行此回掉更新系统的显示 例如渲染层使用它驱动渲染管道
///
/// * _Post-frame callbacks_,在持久回调之后运行,在从[Window.onDrawFrame]回调返回之前。
///
/// * 非渲染任务,在帧之间运行。 它们具有优先级,并根据[schedulingStrategy]按优先级顺序执行。

下面看一下初始化代码

void initInstances() {
    super.initInstances();
    _instance = this;
    window.onBeginFrame = _handleBeginFrame;//给window添加了两个回调
    window.onDrawFrame = _handleDrawFrame;
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);//添加了处理生命周期的回调
    readInitialLifecycleStateFromNativeWindow();
        ···
  }

这个任务调度的类有以下几个属性

enum SchedulerPhase {
  idle,//空闲状态
  transientCallbacks,//临时回调正在运行
  midFrameMicrotasks,//临时回调结束,此状态下会运行一些Microtasks的任务
  persistentCallbacks,//持久回调(handleDrawFrame调用,进行构建/布局/绘制
  postFrameCallbacks,//清理,安排下一帧
}

下面我们按照类注释中标示的几种任务依次查看,首先就是由window.onBeginFrame触发的临时回掉

由初始化的方法可以知道,onBeginFrame会调用这里的_handleBeginFrame

void _handleBeginFrame(Duration rawTimeStamp) {
    handleBeginFrame(rawTimeStamp);
  }
//处理
void handleBeginFrame(Duration rawTimeStamp) {
        ...省略代码
    assert(schedulerPhase == SchedulerPhase.idle);//当前是不是空闲阶段
    _hasScheduledFrame = false;
    try {
      // TRANSIENT FRAME CALLBACKS
      Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
      _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;//结束临时回掉状态
    }
  }
//调用一个临时回掉
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace callbackStack ]) {
   ···
    try {
      callback(timeStamp);//使用时间戳调用callback
    } 
  ···
  }

下面就是_handleDrawFrame方法 从名字上就知道,在里面我们绘制一帧图片

 void _handleDrawFrame() {
    handleDrawFrame();
  }
void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);//是否完成了临时回掉状态
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;//进入持续回掉状态
      for (FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);//先对_persistentCallbacks中的回掉循环处理

      // POST-FRAME CALLBACKS//然后再运行之前提到的post-frame回掉
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;//进入postFrame回掉状态
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);//依次调用
    } finally {
      _schedulerPhase = SchedulerPhase.idle;//结束,空闲状态
      Timeline.finishSync(); // end the Frame
      assert(() {
        if (debugPrintEndFrameBanner)
          debugPrint('▀' * _debugBanner.length);
        _debugBanner = null;
        return true;
      }());
      _currentFrameTimeStamp = null;
    }
  }

由此可见,schedulerbinding在一帧的绘制中,并不负责如何绘制,只负责掌握绘制方法的调用时机

下面我们再看一下这个方法

void ensureVisualUpdate() {//确保视图更新
  switch (schedulerPhase) {
    case SchedulerPhase.idle:
    case SchedulerPhase.postFrameCallbacks:
      scheduleFrame();//如果当前是闲置或者postFrame状态
      return;
    case SchedulerPhase.transientCallbacks:
    case SchedulerPhase.midFrameMicrotasks:
    case SchedulerPhase.persistentCallbacks:
      return;
  }
}
void scheduleFrame() {//调用此方法后,引擎就会调用handleBeginFrame来开始我们一帧的绘制了
    if (_hasScheduledFrame || !_framesEnabled)
      return;
    assert(() {
      if (debugPrintScheduleFrameStacks)
        debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
      return true;
    }());
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }

另外还有一个方法

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

  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  // We use timers here to ensure that microtasks flush in between.
  Timer.run(() {
    assert(_warmUpFrame);
    handleBeginFrame(null);//开始这一帧
  });
  Timer.run(() {
    assert(_warmUpFrame);
    handleDrawFrame();//绘制这一帧
    // We call resetEpoch after this frame so that, in the hot reload case,
    // the very next frame pretends to have occurred immediately after this
    // warm-up frame. The warm-up frame's timestamp will typically be far in
    // the past (the time of the last real frame), so if we didn't reset the
    // epoch we would see a sudden jump from the old time in the warm-up frame
    // to the new time in the "real" frame. The biggest problem with this is
    // that implicit animations end up being triggered at the old time and
    // then skipping every frame and finishing in the new time.
    resetEpoch();
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });
  // Lock events so touch events etc don't insert themselves until the
  // scheduled frame has finished.
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}

上面这个方法是立刻手动调用了绘制一帧的操作,用于系统启动时调用
还有一些其他的任务调度等,我们就先不关注了,感兴趣的同学可以自己去看看
接下来看下一个

PaintingBinding
mixin PaintingBinding on BindingBase, ServicesBinding {//这里可以看到,它同时需要ServicesBinding
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();//建立图像缓存
    if (shaderWarmUp != null) {
      shaderWarmUp.execute();//运行预热着色器
    }
  }
  
  static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();//默认着色器的预热

  @protected
  ImageCache createImageCache() => ImageCache();

  /// Calls through to [dart:ui] with [decodedCacheRatioCap] from [ImageCache].
  Future<ui.Codec> instantiateImageCodec(Uint8List list) {
    return ui.instantiateImageCodec(list);
  }

  @override
  void evict(String asset) {
    super.evict(asset);
    imageCache.clear();
  }
}

这一块代码不是很多,简单看一下,不深入分析了,以后再讲
然后按照顺序来就是

SemanticsBinding
mixin SemanticsBinding on BindingBase {
  static SemanticsBinding get instance => _instance;
  static SemanticsBinding _instance;

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _accessibilityFeatures = window.accessibilityFeatures;//辅助功能相关
  }

  @protected
  void handleAccessibilityFeaturesChanged() {
    _accessibilityFeatures = window.accessibilityFeatures;
  }

  ui.AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures;
  ui.AccessibilityFeatures _accessibilityFeatures;

  bool get disableAnimations {
    bool value = _accessibilityFeatures.disableAnimations;
    assert(() {
      if (debugSemanticsDisableAnimations != null)
        value = debugSemanticsDisableAnimations;
      return true;
    }());
    return value;
  }
}

下面把关注点放在接下来的两个类上

已经分析了5/7,是不是要结束了?错❌,才刚刚开始,我觉得接下来的这两个类距离我们就比较近了,值得好好看看

RendererBinding

听名字就知道了,和渲染相关

我们看先一下它的初始化

void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );//生成了一个pipelineOwner的实例,并给他添加了相关的回掉
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
  //给window设置了回掉
    initRenderView();//初始化renderView
    _handleSemanticsEnabledChanged();//处理语意改变
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);//在这里给我们之前看到的持续回掉任务队列添加了一个任务
    _mouseTracker = _createMouseTracker();//鼠标追踪
  }

上面涉及了几个类和方法,我们先依次看一下 首先就是

PipeLineOwner

先看看注释

/// 用于管理渲染中的管道
/// 提供了接口用于驱动渲染管道,并在管道的每个阶段存储渲染对象请求访问的状态。要刷新管道,请按顺序调
/// 用以下函数:
///
/// 1. [flushLayout]更新需要计算布局的渲染对象。在此阶段,计算每个渲染对象的大小和位置。此阶段渲
/// 染对象可能会弄脏他们的绘画或合成状态
/// 2. [flushCompositingBits]更新具有脏合成位的任何渲染对象。在此阶段,每个渲染对象都会了解其子
/// 项是否需要合成。在绘制阶段使用此信息时,选择如何实现剪裁等视觉效果。如果渲染对象具有合成子对象,
/// 则需要使用[Layer图层]创建剪辑,以便将剪辑应用于合成子对象(将其绘制到自己的[图层]中)。
/// 3. [flushPaint]访问需要绘制的任何渲染对象。在此阶段,渲染对象有机会将绘制命令记录到
/// [PictureLayer]并构造其他合成的[Layer]。
/// 4.最后,如果启用了语义,[flushSemantics]将编译渲染对象的语义。辅助技术使用该语义信息来改进渲
/// 染树的可访问性。

简单说,就是渲染对象的绘制管理类,我们看一下注释提到的几个方法//省略了一些断言和其他代码

void flushLayout() {
    while (_nodesNeedingLayout.isNotEmpty) {
      final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
      _nodesNeedingLayout = <RenderObject>[];
      for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
        if (node._needsLayout && node.owner == this)
          node._layoutWithoutResize();//对需要layout的渲染对象按照深度依次测量布局
      }
    }
}
void flushCompositingBits() {
  _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
  for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
    if (node._needsCompositingBitsUpdate && node.owner == this)
      node._updateCompositingBits();//对需要更新合成的对象依次更新
  }
  _nodesNeedingCompositingBitsUpdate.clear();
}
void flushPaint() {
    final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
    _nodesNeedingPaint = <RenderObject>[];
    // Sort the dirty nodes in reverse order (deepest first).
    for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
      assert(node._layer != null);
      if (node._needsPaint && node.owner == this) {
        if (node._layer.attached) {
          PaintingContext.repaintCompositedChild(node);//调用此方法绘制节点,这里从叶子节点开始
        } else {
          node._skippedPaintingOnLayer();
        }
      }
    }
    assert(_nodesNeedingPaint.isEmpty);
}

下面看一下初始化RenderView的相关操作 因为目的是了解流程,我同样省略了一些断言判断等代码

RenderView
RendererBinding====
 void initRenderView() {
     assert(renderView == null);
     renderView = RenderView(configuration: createViewConfiguration(), window: window);
     renderView.scheduleInitialFrame();//初始化Frame
   }
//上面这段代码中,renderView = RenderView(configuration: createViewConfiguration(), window: window);并不是简单的赋值,实际上RendererBinding重写了renderView的set方法,我们看
set renderView(RenderView value) {
    assert(value != null);
    _pipelineOwner.rootNode = value;//可以看到这里实际上是把生成的RenderView设置给pipeLineOwner。而pipelineOwner的rootNode 的set方法也是设置的
  }
set rootNode(AbstractNode value) {
    if (_rootNode == value)
      return;
    _rootNode?.detach();
    _rootNode = value;
    _rootNode?.attach(this);//可以看到,调用了renderView的attach方法 这个方法具体我们下两篇再说
}
RenderView====
 void scheduleInitialFrame() {
    assert(owner != null);
    assert(_rootTransform == null);
    scheduleInitialLayout();//提交初始化布局的任务
    scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());//提交初始化绘制的任务
    assert(_rootTransform != null);
    owner.requestVisualUpdate();//请求视图更新
  }
  void scheduleInitialLayout() {
    owner._nodesNeedingLayout.add(this);//把自己添加到管道管理者需要布局的list中,前面见到过
  }
  void scheduleInitialPaint(ContainerLayer rootLayer) {
    _layer = rootLayer;
    owner._nodesNeedingPaint.add(this);//加入需要绘制的list中
  }
PipeLineOwner====
  void requestVisualUpdate() {
    if (onNeedVisualUpdate != null)
      onNeedVisualUpdate();//这个回调正是binding初始化的时候传进来的,实际上这个方法并不是RendererBinding实现的,而是SchedulerBinding实现的,我们之前看过了,这个方法就会触发一帧的开始
  }

最后我们看一下添加给任务调度的一个回调方法

_handlePersistentFrameCallback
void _handlePersistentFrameCallback(Duration timeStamp) {
    drawFrame();//直接调用了drawframe方法
  }
drawFrame

其实这个方法的代码并不多,先看一下

  @protected
  void drawFrame() {
    assert(renderView != null);
    pipelineOwner.flushLayout();//刷新布局
    pipelineOwner.flushCompositingBits();//刷新组合
    pipelineOwner.flushPaint();//刷新绘制
    renderView.compositeFrame(); // 发送bit给Gpu
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
  }

可以看到实际上就是按照pipelineowner要求的顺序刷新绘制

对这个渲染binding的分析目前就到这里,我们来看最后一个类

WidgetsBinding

顾名思义,就是widget层和Flutter引擎的binding 看了这么多的类,终于遇到widget了,说好的一切都是widget呢哈哈

首先还是看一下初始化方法

void initInstances() {
    super.initInstances();
    _instance = this;
    buildOwner.onBuildScheduled = _handleBuildScheduled;//给buildOwner添加回掉
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;//添加回掉
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    SystemChannels.system.setMessageHandler(_handleSystemMessage);
  }

可以看到主要就是添加了一些回掉,我们分析一下

首先我们看到了buildOwner,这是啥呢

BuildOwner

还是先看一下注释

/// widgets框架的管理者
/// 此类跟踪需要重构的widgets 并处理其他适用于widget tree整体的任务,例如管理树的非活动元素列表,并在调试时在
/// 热重新加载期间触发“重组”命令。
///
/// 主buildowner通常由WidgetsBinding拥有,并且由操作系统随构建/布局/绘制管道以外的部分驱动。
///
/// 可以构建额外的buildowner用来管理屏幕以外的widget tree
///
/// 要将buildowner分配给树,请在widget树的root上使用[RootRenderObjectElement.assignOwner]方法。

然后我们看看他有啥功能

先是构造函数

BuildOwner({ this.onBuildScheduled });//没啥特别的,可以传一个回掉

下面我们来看两个比较长的函数

1.buildscope

首先看看注释,看他是干啥的

    建立更新widget树的范围并回调callback(如果有的话),然后按照深度顺序构建通过scheduleBuildFor方法标记为dirty的element。
    这种机制可以防止构造方法递归导致死循环
  在`callback`返回后处理dirty列表。如果在此方法运行时元素被标记为脏,则它们必须比`context`节点更深,并且比此传递中的任何先前构建的节点更深。
    要刷新当前的dirty列表单不执行其他操作,可以调用此方法但不传callbak参数。每一帧在WidgetsBinding.drawFrame中都会这么做
    同一时间只能调用一次
  [buildScope]也意味着[lockState]范围。
  要在每次调用此方法时打印控制台消息,请将[debugPrintBuildScope]设置为true。 这在调试涉及widget没有被标记为脏的问题或者过于频繁地标记为脏时很有用。

下面我们看一下方法实现 比较长,我省略一些断言或者打印或者其他代码

void buildScope(Element context, [ VoidCallback callback ]) {
    try {
      _scheduledFlushDirtyElements = true;//标记正在刷新dirty元素
      if (callback != null) {//如果有callback的话
        Element debugPreviousBuildTarget;
        _dirtyElementsNeedsResorting = false;
        try {
          callback();//先调用callback的方法
        } finally {
          ···
        }
      }
      _dirtyElements.sort(Element._sort);//按照深度排序
      _dirtyElementsNeedsResorting = false;
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {//循环
        ···
        try {
          _dirtyElements[index].rebuild();//依次rebuild
        } catch (e, stack) {}
        index += 1;
        if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
          _dirtyElements.sort(Element._sort);
          _dirtyElementsNeedsResorting = false;
          dirtyCount = _dirtyElements.length;
          while (index > 0 && _dirtyElements[index - 1].dirty) {//回漱查找
            index -= 1;
          }
        }
      }
      ···
    } finally {
      for (Element element in _dirtyElements) {
        assert(element._inDirtyList);
        element._inDirtyList = false;//放出来
      }
      _dirtyElements.clear();//清除
      _scheduledFlushDirtyElements = false;
      _dirtyElementsNeedsResorting = null;
      Timeline.finishSync();
      ···
    }
  }

544
2.下一个就是前面说的用来标记element dirty的方法

///添加一个element到dirtylist中,等 WidgetsBinding.drawFrame调用buildScope时就会被重建
  void scheduleBuildFor(Element element) {
    if (element._inDirtyList) { //已经在了
      _dirtyElementsNeedsResorting = true;//需要重新排序
      return;
    }
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {//没有正在刷新
      _scheduledFlushDirtyElements = true;//开始刷新
      onBuildScheduled();//回掉
    }
    _dirtyElements.add(element);//添加进去
    element._inDirtyList = true;
  }

下面我们就看看WeigetsBinding的回掉onBuildScheduled

void _handleBuildScheduled() {
  ensureVisualUpdate();//调用SchedulerBinding的刷新视图任务
}

3.看一下释放tree

void finalizeTree() {
    try {
      lockState(() {
        _inactiveElements._unmountAll(); //在这里把所有不活跃状态的element卸载
      });
  }

接下来我们看一下前面多次提到的drawFrame

drawFrame

看名字就知道,这里开始绘制一帧,实际上在前面我们已经见过这个方法了,就是在RendererBinding中,这里对它惊醒了重写。那么是哪里调用的呢?大家可能忘了,还是在是在之前的rendererBinding中

void _handlePersistentFrameCallback(Duration timeStamp) {
  drawFrame();//开始绘制这一帧
}

那我们看一下这个方法的代码

@override
void drawFrame() {
    if (renderViewElement != null)
      buildOwner.buildScope(renderViewElement);//建立更新跟节点的范围
    super.drawFrame();//调用RendererBinding开始绘制一帧
    buildOwner.finalizeTree();//卸载没用的element
}
attachRootWidget
void attachRootWidget(Widget rootWidget) {//通过此方法添加了根节点
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement);
}

好,到此为止~整个Binding们基本看完了,我们最后梳理一下合体之后初始化的整体

WidgetsFlutterBinding

首先我们回顾一下他的继承顺序

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

按照顺序花了一张流程图,分析的不对的话还希望有老哥帮忙指出


Binding.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容