Flutter RendererBinding作用源码分析

目录
  • 分析
  • PipelineOwner
    • flushLayout
    • flushCompositingBits
    • flushPaint
    • flushSemantics
  • initRenderView
    • scheduleInitialLayout
    • scheduleInitialPaint

分析

RendererBinding 的作用是负责render tree 和flutter engine之间的连接. 我们在启动App的时候,首先会创建 PiplineOwner ,然后通过platformDispatcher去监听屏幕分辨率变化、系统文字大小变化、亮度、语义等等.最后去初始化RenderView,根据平台去处理如帧回调、鼠标、web之类的信息.

 void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    platformDispatcher
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }

PipelineOwner

这里我们着重讲一下PipelineOwner, 官方描述中有这么一句话The pipeline owner manages the rendering pipeline., 也就是说 PipelineOwner帮我们管理着渲染所需要的. 我们根据PipelineOwner调用的顺序依次讲解下它提供的方法.

flushLayout

这个阶段将计算每个渲染对象的大小和位置, 渲染对象可能会在绘制或者compositing state 的时候被标成dirty,这是什么意思呢? 让我们回归到代码中

  // 持有需要被布局的对象
  List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
{
    非release包运行代码忽略
    ...
    try {
      // 如果当前的节点需要合成
      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();
        }
      }
    } finally {
        ...
    }
}

代码中可以看到, 如果 _nodesNeedingLayout 中对象不为空.说明当前需要被布局,计算其大小.我们可以看到在依次处理节点时,最后一步是执行 _layoutWithoutResize() ,这个方法调用的本质实际上也就是 performLayout(). 那么, performLayout() 做了什么呢? 如果对自定义布局有过了解, 通常我们在实现 performLayout() 的时候.会先去 layout widget . 然后去通过position将widget 定位. 确定好widget在父widget中的相对位置.

flushCompositingBits

这个阶段中, 每个渲染对象都会了解其子对象是否需要合成.在绘制的阶段选择如何实现视觉效果. 这里实际上也就是标记所有的子对象是否需要合成

  void flushCompositingBits() {
    ...
    _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();
   ...
  }

flushPaint

在这个阶段,我们将会真正的绘制出Layer

  void flushPaint() {
    ...
    try {
      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._layerHandle.layer!.attached) {
            PaintingContext.repaintCompositedChild(node);
          } else {
            node._skippedPaintingOnLayer();
          }
        }
      }
      ...
    } finally {
      ...
    }
  }

在绘制的时候将会判断当前的layer是否attached,如果不是attched的状态.则说明当前的layer已经调用detach方法,因此不再需要绘制.所以会跳过绘制,执行 _skippedPaintingOnLayer() 的方法. 如果是attached的状态,则需要调用 repaintCompositedChild() 的方法

flushSemantics

最后,如果启用了语义. 这个阶段将会编译渲染对象的语义,这里就不过多介绍了.

initRenderView

如果说还有比较重要的方法需要讲解, 那么就是 initRenderView() 这个方法了.这里将会创建一个 RenderView的对象作为RenderObject的根 ,同时对它进行初始化.

  void initRenderView() {
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    // 准备第一帧启动渲染通道. 这里只会调用一次.
    renderView.prepareInitialFrame();
  }

在 PrepareInitalFrame() 中, 我们通过 scheduleInitialLayout和scheduleInitialPaint , 安排微事务队列尽可能快的处理layout和paint.

scheduleInitialLayout

在这个阶段,主要是将owner的_nodesNeedingLayout 对象中加入这个初始化的renderview.

scheduleInitialPaint

这个阶段中, 我们将_layerHandle 中的layer 赋值成当前layer.并在owner中加入 _nodesNeedingPaint .

  void scheduleInitialPaint(ContainerLayer rootLayer) {
    _layerHandle.layer = rootLayer;
    owner!._nodesNeedingPaint.add(this);
  }

今天的RendererBinding源码分析就暂告一个段落了,它主要是负责了测量布局、绘制之类的方法. 作为一个入口还是有了解的必要的, 建议大家有时间可以多看看.

以上就是Flutter RendererBinding作用源码分析的详细内容,更多关于Flutter RendererBinding的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter项目手势运用及单独指针消歧问题解决方案

    目录 对于手势的运用 单独指针 消歧 依赖包中的字体 对于手势的运用 指针表示用户与设备屏幕交互的原始数据.有四种类型的指针事件 PointerDownEvent指针触摸屏幕上的特定位置 PointerMoveEvent指针从屏幕上的一个位置移动到另一个位置 PointerUpEvent指针停止触摸屏幕 PointerCancelEvent指针的输入事件不再针对此应用程序(事件取消) Widget build(BuildContext context) { return new GestureD

  • Flutter框架解决盒约束widget和assets里加载资产技术

    目录 盒约束 文本输入widget assets 加载资产 盒约束 flutter: assets: - assets/my_icon.png - assets/background.png 在Flutter中,小部件由其底层RenderBox对象渲染.渲染框受其父对象的约束,并在这些约束下调整自身大小.约束包括最小宽度.最大宽度和高度:尺寸由特定的宽度和高度组成. 通常,根据小部件如何处理其约束,有三种类型的框: 尽可能大.例如,“Center”和“ListView”的渲染框 遵循子部件的大小

  • Flutter图片缓存管理ImageCache原理分析

    目录 引言 PaintingBinding 减少图片缓存 增大阀值 思考 引言 设计: 嗯? 这个图片点击跳转进详情再返回图片怎么变白闪一下呢?产品: 是啊是啊! 一定是个bug开发: 囧囧囧 在开发过程中, 也许你也遇到过这样一个场景. 进入一个页面后,前一个页面的图片都会闪白一下. 或者在列表中,加载很多列表项后,之前列表中的图片都需要重新加载.你有没有想过这一切的原因是什么呢? 没错! 它就是我们今天介绍的主人公 --- ImageCache 可能有些人对ImageCache还有些陌生,

  • 基于Flutter实现手势密码加密与解锁功能

    目录 前言 1.绘制静态图形 2.存储手势密码数据 3.添加手势交互 4.绘制.刷新密码线 5.加入密码错误动画 总结 前言 密码的由来:在公元前405年,由古希腊和斯巴达的战争中,由于斯巴达盟友波斯帝国背叛,导致古希腊和斯巴达两败俱伤,这时斯巴达抓了一个波斯国的信使,这个信使 没有任何情报,只有一条有着杂乱无章的希腊字母的普通腰带,最终斯巴达统帅破解了这条腰带,成功击败了希腊.这就是世界上最早的密码.同时也是世界上最早的解密. 密码在我们生活中无处不在,作为个人隐私的最后一道防线显得无比的重要

  • Flutter实现手势识别功能详解方法

    目录 GestureDetector 点击事件 双击事件 长按事件 水平/垂直拖动事件 缩放事件 InkWell Ink Listener 案例 GestureDetector GestureDetector 是手势识别的组件,可以识别点击.双击.长按事件.拖动.缩放等手势 点击事件 点击相关事件包括: onTapDown:按下时回调. onTapUp:抬起时回调. onTap:点击事件回调. onTapCancel:点击取消事件回调. 按下然后抬起调用顺序 onTapDown-> onTapU

  • Flutter手势密码的实现示例(附demo)

    目录 前言 开始 绘制圆点 绘制手势路径 组合9个圆点盘和手势路径 手势密码组件的使用 上传自定义组件到pub仓库 后记 前言 本篇记录的是使用Flutter完成手势密码的功能,大致效果如下图所示: 该手势密码的功能比较简单,下面会详细记录实现的过程,另外还会简单说明如何将该手势密码作为插件发布到pub仓库. 开始 实现上面的手势密码并不难,大致可以拆分成如下几部分来完成: 绘制9个圆点 绘制手指滑动的线路 合并以上两个部分 绘制圆点 我们使用面向对象的方式来处理9个圆点的绘制,每个圆点作为一个

  • flutter消息推送客户端集成方案详解

    目录 一.背景 二.第三方消息推送——个推 1.简介 2.注册开通 3.自定义消息推送——透传 三.项目集成 1.个推客户端flutter插件 2.Android和IOS配置 1)Android 2) iOS 3.通知栏插件flutter_local_notifications 4.个推消息与通知栏整合 最后 一.背景 公司一个CRM APP项目是用Flutter写的,根据业务要求,需要集成消息推送功能.所谓的消息推送就是系统会根据某些行为自动推送信息,手机的通知栏会接收到信息,点击可以打开ap

  • Flutter源码分析之自定义控件(RenderBox)指南

    目录 前言 RenderObject 类继承层级解析 RenderBox 叶节点与父节点 控件的测量与布局 performResize 和 performLayout relayoutBoundary 叶节点 父节点 ParentData ParentData BoxParentData ContainerBoxParentData ContainerParentDataMixin 测量 child 大小 布局 child 控件的绘制 绘制自身内容 绘制 child repaintBoundar

  • Vue编译器源码分析compileToFunctions作用详解

    目录 引言 Vue.prototype.$mount函数体 源码出处 options.delimiters & options.comments compileToFunctions函数逐行分析 createFunction 函数源码 引言 Vue编译器源码分析 接上篇文章我们来分析:compileToFunctions的作用. 经过前面的讲解,我们已经知道了 compileToFunctions 的真正来源你可能会问为什么要弄的这么复杂?为了搞清楚这个问题,我们还需要继续接触完整的代码. 下面

  • MyBatis 源码分析 之SqlSession接口和Executor类

    mybatis框架在操作数据的时候,离不开SqlSession接口实例类的作用.可以说SqlSession接口实例是开发过程中打交道最多的一个类.即是DefaultSqlSession类.如果笔者记得没有错的话,早期是没有什么getMapper方法的.增删改查各志有对应的方法进行操作.虽然现在改进了很多,但是也保留了很多.我们依旧可以看到类似于selectList这样子的方法.源码的例子里面就可以找到.如下 SqlSession session = sqlMapper.openSession(T

  • 深入浅析knockout源码分析之订阅

    Knockout.js是什么? Knockout是一款很优秀的JavaScript库,它可以帮助你仅使用一个清晰整洁的底层数据模型(data model)即可创建一个富文本且具有良好的显示和编辑功能的用户界面.任何时候你的局部UI内容需要自动更新(比如:依赖于用户行为的改变或者外部的数据源发生变化),KO都可以很简单的帮你实现,并且非常易于维护. 一.主类关系图 二.类职责 2.1.observable(普通监控对象类) observable(他其是一个function)的内部实现: 1.首先声

  • jQuery 1.9.1源码分析系列(十四)之常用jQuery工具

    为了给下一章分析动画处理做准备,先来看一下一些工具.其中队列工具在动画处理中被经常使用. jQuery.fn. queue(([ queueName ] [, newQueue ]) || ([ queueName ,] callback ))(获取或设置当前匹配元素上待执行的函数队列. 如果当前jQuery对象匹配多个元素:获取队列时,只获取第一个匹配元素上的队列:设置队列(替换队列.追加函数)时,则为每个匹配元素都分别进行设置.如果需要移除并执行队列中的第一个函数,请使用dequeue()函

  • jQuery源码分析之jQuery.fn.each与jQuery.each用法

    本文实例讲述了jQuery源码分析之jQuery.fn.each与jQuery.each用法.分享给大家供大家参考.具体分析如下: 先上例子,下面代码的作用是:对每个选中的div元素,都给它们添加一个red类 复制代码 代码如下: $('div').each(function(index, elem){       $(this).addClass('red'); } }); 上面用的的.each,即jQuery.fn.each,其内部是通过jQuery.each实现的 复制代码 代码如下: j

  • jQuery-1.9.1源码分析系列(十)事件系统之事件包装

    在上篇文章给大家介绍了jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构,本篇继续给大家介绍jquery1.9.1源码分析系列相关知识,具体内容请看下文吧. 首先需要明白,浏览器的原生事件是只读的,限制了jQuery对他的操作.举个简单的例子就能明白为什么jQuery非要构造一个新的事件对象. 在委托处理中,a节点委托b节点在a被click的时候执行fn函数.当事件冒泡到b节点,执行fn的时候上下文环境需要保证正确,是a节点执行了fn而非b节点.如何保证执行fn的上下文环境是a节点

  • jQuery插件-jRating评分插件源码分析及使用方法

    该插件被广泛应用于各种需要评分的页面当中,今天作为学习,把源码拿出来分析一下,顺便学习其使用方法. 一.插件使用一览. 复制代码 代码如下: <div> <div>第一个例子</div> <div id="16_1" class="myRating"></div> </div> 复制代码 代码如下: <link href="Script/jRating/jRating.jquer

  • java并发容器CopyOnWriteArrayList实现原理及源码分析

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet.本文会对CopyOnWriteArrayList的实现原理及源码进行分析. 实现原理 我们都知道,集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但由于简单粗暴的锁同步机制,

  • PipedWriter和PipedReader源码分析_动力节点Java学院整理

    PipedWriter和PipedReader源码分析 1. PipedWriter 源码(基于jdk1.7.40)  package java.io; public class PipedWriter extends Writer { // 与PipedWriter通信的PipedReader对象 private PipedReader sink; // PipedWriter的关闭标记 private boolean closed = false; // 构造函数,指定配对的PipedRea

随机推荐