Flutter 绘制原理

Flutter框架

 

从图中可以看出 Flutter主要被分为两层 Framework层和Flutter Engine.

Framework层全部使用Dart编写,有完整UI框架的API,并预写了Android(MaterialDesign)和IOS的(Cupertino)风格的UI,极大方便了开发移动端.

Framework 底层是 Flutter 引擎, 引擎主要负责图形绘制 (Skia)、 文字排版 (libtxt) 和提供 Dart 运行时, 引擎全部使用 C++实现.

Flutter高性能原理

与其他跨平台框架对比

在看Flutter框架前,我们先看一下其他跨平台框架的设计

 

看Hybrid的架构,我们可以知道UI层的渲染是基于Webview去渲染,他的性能取决于webview的渲染性能,目前已知webview渲染性能 < NativeUI的性能

 

RN/Weex 的架构中,是基于Native的UI框架去适配,中间多了一层js转NativeUI的过程


 

 

而Flutter不需要中间层(Webview,js 转NativeUI这个过程),他是基于图像渲染引擎去直接绘制UI.

Dart 对于UI框架的高性能支持

我们知道Flutter的Framework层是使用了Dart语言编写,那Dart语言有哪些优势呢?下面分为几个点来阐述

Dart内存分配机制

DartVM的内存分配策略非常简单,创建对象时只需要在现有堆上移动指针,内存增长始终是线形的,省去了查找可用内存段的过程

 

Dart中类似线程的概念叫做Isolate,每个Isolate之间是无法共享内存的,所以这种分配策略可以让Dart实现无锁的快速分配。

Dart 垃圾回收机制

Dart的垃圾回收也采用了多生代算法,新生代在回收内存时采用了“半空间”算法,触发垃圾回收时Dart会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存如图.

 

整个过程中Dart只需要操作少量的“活跃”对象,大量的没有引用的“死亡”对象则被忽略,这种 多生代无锁垃圾回收器,专门为UI框架中常见的大量Widgets对象创建和销毁优化,非常适合Flutter框架中大量Widget重建的场景.

Dart 编体积优化,及编译JIT和AOT支持

代码体积优化(Tree Shaking),编译时只保留运行时需要调用的代码(不允许反射这样的隐式引用),所以庞大的Widgets库不会造成发布体积过大。

Dart支持两种编译模式:

  • JIT编译 Just In Time Compiler -即时编译
  • AOT编译Ahead Of Time 预编译

在debug模式下使用JIT编译,生成srcipt/bytecode进行解释执行,可以支持HotReload(热重载),修改代码,保持即可在设备上看到效果. 而在Release下 AOT编译生成Machine Code,高效的运行.

Dart 单线程 异步消息机制

客户端交互简述

对于移动端的交互来说,大多数情况下都是在等待状态,等待网络请求,等待用户输入等.那么设想一下,发起一个网络请求只在一个线程中可以进行吗?当然网络请求肯定是异步的(注意这里说的异步而多线程并非一个概念.),事实验证是可以的,Flutter就采用了Dart这种单线程机制,省去了多线程上下文切换带来的性能损耗.(对于高耗时操作,也同样支持多线程操作,通过Isolate开启,不过注意这里的多线程,内存是无法共享的.)

Dart 异步消息原理

当一个Dart的方法开始执行时,他会一直执行直至达到这个方法的退出点。换句话说Dart的方法是不会被其他Dart代码打断的。
当一个Dart应用开始的标志是它的main isolate执行了main方法。当main方法退出后,main isolate的线程就会去逐一处理消息队列中的消息。

 

有了消息队列,然后有了循环去读取消息队列中的消息,就可以有单线程去执行异步消息的能力.
一般的消息使用dart:async中使用Future来支持异步消息.

Flutter Engine 高性能

在讲Flutter Engin层时,我们先讲一下屏幕绘制的原理.

屏幕绘制原理

 

我们都知道显示器以固定的频率刷新,比如 iPhone的 60Hz、iPad Pro的 120Hz。当一帧图像绘制完毕后准备绘制下一帧时,显示器会发出一个垂直同步信号(VSync),所以 60Hz的屏幕就会一秒内发出 60次这样的信号。

并且一般地来说,计算机系统中,CPU、GPU和显示器以一种特定的方式协作:CPU将计算好的显示内容提交给 GPU,GPU渲染后放入帧缓冲区,然后视频控制器按照 VSync信号从帧缓冲区取帧数据传递给显示器显示。

作为一个专职Android开发,看过Android的绘图机制,通过SurfaceFlinger 和HAL层之间的工作机制发现和Flutter的很像,那么IOS的如何呢?个人推测屏幕的绘图机制是一样的,只是不同平台有不同实现.

Flutter Engine的渲染机制

 

Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU.

所以 Flutter并不关心显示器、视频控制器以及 GPU具体工作,它只关心 GPU发出的 VSync信号,尽可能快地在两个 VSync信号之间计算并合成视图数据,并且把数据提供给 GPU.

Flutter Framework层的绘图机制

UI树原理

在 Flutter 界面渲染过程分为 3 个阶段: 布局、绘制、合成.

而布局阶段,有三个重要的对象.RenderObject、Element、Widget.

 

 

  • Widget是开发经常接触的控件,默认是只读的.

  • Element 是 Flutter 用来分离控件树和真正的渲染 对象的中间层, 控件用来描述对应的 element 属性,控件重建后可能会复用同一个 element.

  • RenderObject 负责提供配置信息并创建具体的 Element。

Element 持有真正负责布局、 绘制和碰撞测试 (hit test) 的 RenderObject 对象.

那么这样,如果控件的属性发生了变化 (因为控件的属性是只 读的, 所以变化也就意味着重新创建了新的控件树), 但是其树上每个节点的类型没有变化时, element 树和 render 树可以完全重用原来的对象 (因为 element 和 render object 的属性都是可变的)

布局原理

传统布局,如Android可能需要多次Measure,计算宽高。Flutter 采用约束进行单次测量布局. 整个布局过程中只需要深度遍历一次,极大的提升效能。

 

渲染对象树中的每个对象都会在布局过程中接受父 对象的 Constraints 参数,决定自己的大小, 然后父对象 就可以按照自己的逻辑决定各个子对象的位置,完成布局过程.

子对象不存储自己在容器中的位置, 所以在它的位置发生改变时并不需要重新布局或者绘制. 子对象的位 置信息存储在它自己的 parentData 字段中,但是该字段由它的父对象负责维护,自身并不关心该字段的内容。

同时也因为这种简单的布局逻辑, Flutter 可以在某些节 点设置布局边界 (Relayout boundary), 即当边界内的任 何对象发生重新布局时, 不会影响边界外的对象, 反之亦然.

参考资料

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值