美文网首页Flutter小笔记
从Flutter的Binding们说起

从Flutter的Binding们说起

作者: 李发糕 | 来源:发表于2019-07-19 22:25 被阅读0次

    关于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

    相关文章

      网友评论

        本文标题:从Flutter的Binding们说起

        本文链接:https://www.haomeiwen.com/subject/hffelctx.html