Flutter 高性能原理浅析

2022-01-21  本文已影响0人  小农民老霍儿

Flutter 高性能原理浅析

[图片上传失败...(image-1a480b-1642738386010)]

北斗星_And关注

1<time datetime="2019-07-29T01:34:03.000Z" style="box-sizing: border-box; margin-right: 10px;">2019.07.29 09:34:03</time>字数 2,470阅读 4,273

<article class="_2rhmJa" style="box-sizing: border-box; display: block; font-weight: 400; line-height: 1.8; margin-bottom: 20px; word-break: break-word; position: relative;">

这是我第三篇Flutter相关博客
欢迎 查看我的前两篇 Flutter实现篇

前言

Flutter是Google用以帮助开发者在Ios和Android两个平台开发高质量原生应用的全新移动UI框架.我开始认识Flutter时,经历了三个Flutter重要历史版本.

此后越来越多的人开始关注到Flutter。

在 Flutter 诞生之前,已经有许多跨平台 UI 框架的方案,比如基于 WebView 的 Cordova、AppCan 等,还有使用 HTML+JavaScript 渲染成原生控件的 React Native、Weex 等。Flutter 则开辟了一种全新的思路,从头到 尾重写一套跨平台的 UI 框架,包括 UI 控件、渲染逻辑甚至开发语言。

Flutter框架

image

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

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

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

Flutter高性能原理

与其他跨平台框架对比

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

image

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

image

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

image

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

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

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

Dart内存分配机制

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

image

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

Dart 垃圾回收机制

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

image

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

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

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

Dart支持两种编译模式:

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

Dart 单线程 异步消息机制

客户端交互简述

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

Dart 异步消息原理

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

image

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

Flutter Engine 高性能

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

屏幕绘制原理

image

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

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

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

Flutter Engine的渲染机制

image

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

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

Flutter Framework层的绘图机制

UI树原理

image

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

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

image

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

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

布局原理

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

image

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

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

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

</article>

上一篇下一篇

猜你喜欢

热点阅读