Flutter、Golang、Python、编译原理、算法、Chrome原理学习系列文章抢先看请关注【码农帮派】:
Flutter重写了一套包括底层渲染逻辑 + 上层开发语言的完整方案,保证了:
-
高保真:保证视图在Android和IOS平台上高度一致
-
高性能:代码执行的效率媲美原生App的体验
Flutter与其他跨平台方案的区别:
-
RN之类的框架,通过JavaScript虚拟机调用系统原生组件,由Android和IOS系统进行组件的渲染;
-
Flutter是自己完成组件的渲染
在计算机系统中,图像的显示需要CPU、GPU和显示器一起配合完成:
-
CPU负责图像数据的计算
-
GPU负责图像数据的渲染
-
显示器负责最终图像的显示
CPU把计算好的、需要显示的内容交给GPU,由GPU完成渲染后放入帧缓存区,随后由视频控制器根据垂直同步信号(VSync)以每秒60次的速度,从帧缓存区读取帧数据交由显示器完成图像的显示。
操作系统在呈现图像时遵循了这种机制,而Flutter作为跨平台开发框架也采用这种底层方案:
上图中,Flutter 关注如何尽可能快地在两个硬件时钟的 VSync 信号之间计算并合成视图数据,然后通过 Skia 交给 GPU 渲染:UI 线程使用 Dart 来构建视图结构数据,这些数据会在 GPU 线程进行图层合成,随后交给 Skia 引擎加工成 GPU 数据,而这些数据会通过 OpenGL 最终提供给 GPU 渲染。
Skia是一款2D图像绘制引擎,是Android官方图像绘制引擎,因此在Android Flutter中无需嵌入Skia引擎就可以天然得到Skia的支持,而IOS系统中并没有内置Skia引擎,所以Flutter IOS打包的App包体积比Android要大一些。
Dart语言作为Flutter的开发语言:
1. Dart同时支持了JIT和AOT两种编译模式,JIT(Just In Time)即时编译,便于在开发期调试;AOT(Ahead Of Time)事前编译,提高程序执行效率。
2. Dart避免了抢占式调度和共享内存,可以在没有锁的情况下进行对象的分配和垃圾回收,在性能上表现相当不错。
Flutter原理:
Flutter架构采用分层设计,从下到上分为3层:Embedder、Engine、Framework。
Embeder层是操作系统适配层,实现了渲染Surface设置、线程设置,以及平台插件等平台相关特性的适配。
Engine层主要包括Skia、Dart和Text,实现了Flutter的渲染引擎、文字排版、事件处理和Dart运行的能力。Skia和Text为上层接口提供了调用底层渲染和排版的能力,Dart则为Flutter提供了运行时调用Dart和渲染引擎的能力。Engine层则是将它们组合起来,从它们生成的数据中实现视图渲染。
Framework层是一个用Dart实现的UI SDK,包含了动画、图形绘制和手势识别等功能,在Framework层Flutter提供了Material和Cupertino两种风格的组件库。
Flutter工作原理/流程:
Flutter界面中各个元素(Widget)以树的形式组织,即控件树。Flutter通过控件树上的每个控件创建不同类型的渲染对象,组成了渲染对象树。渲染对象树在Flutter的展示过程分为4个阶段:
-
布局
-
绘制
-
合成
-
渲染
布局:
Flutter采用深度优先策略遍历整个渲染对象树,决定渲染对象在屏幕中的位置和尺寸。在布局过程中每个渲染对象都会接收父对象的布局约束参数,决定自己的大小,父对象按照控件布局逻辑决定每个子对象的位置,完成布局过程。
布局边界:为了防止因子节点发生变化而导致整个控件树重新布局,Flutter引入了机制:布局边界(Relayout Boundary),可以在某些节点上自动或手动设置布局边界,当布局边界内的任何对象发生重新布局的时候,都不会影响边界外的对象,反之边界外的对象发生重新布局,也不会影响边界内的对象。
绘制:
布局完成之后,渲染对象树的每个节点都有了明确的尺寸和位置,Flutter会把所有的渲染对象绘制到不同的图层上。绘制的过程也是采用深度优先策略,而且总是先绘制自身,再绘制子节点。
上图中,节点1在绘制完自身后,会再绘制节点2,然后绘制它的子节点3、4 和 5,最后绘制节点6。由于某些原因(e.g. 视图手动合并)导致节点2的子节点5和它的兄弟节点6处于同一层,这样会导致当节点2需要重绘的时候,与其无关的节点6也会被重绘,带来性能的损耗。
为了解决这一问题,Flutter提供了与布局边界对应的机制-重绘边界(Repaint Boundary)。在重绘边界内,Flutter会强制切换新的图层,避免边界内外的节点相互影响,避免无关内容置于同一层引起不必要的重绘。
重绘边界的一个典型场景是 Scrollview。ScrollView 滚动的时候需要刷新视图内容,从而触发内容重绘。而当滚动内容重绘时,一般情况下其他内容是不需要重绘的,这时候重绘边界就派上用场了。
合成和渲染:
当绘制界面复杂的时候,Flutter的渲染树层级通常会很多,直接交付给渲染引擎进行多图层的渲染,可能会出现大量渲染内容重复绘制,所以还需要进行一次图层合并,即将所有的图层按照大小、层级、透明度等规则计算出最终的显示效果,将相同的图层进行归类合并,简化渲染树,提高渲染引擎的渲染效率。
合并完成之后,Flutter会将几何图层数据交由Skia引擎加工成二维图像数据,最终由GPU进行渲染,完成界面的展示。