从里到外,聊一聊 FLutter

Posted by Miaocf on November 2, 2020

1. 什么是flutter

Flutter 是 Google 开源的 UI 工具包,帮助开发者通过一套代码库,
高效构建多平台精美应用,支持移动、Web、桌面和嵌入式平台。

1.1 flutter 优势

  • Flutter 很好的解决了跨端一致性问题,一套代码无差异的同时跑在 iOS 与 Android 两端;
  • 开发体验基本接近前端.
  • 支持 on deviceHot Reload
  • Flutter 在 Android Studio 中通过插件实现实时预览并支持交互的 Hot UI 能力,以及 Layout Explorer 可视化布局,让Flutter 的开发效率和前端效率基本持平。

阿里巴巴淘系技术

2. FLutter 架构

Flutter的整体架构共分为三层,从下到上分别为:Embedder层Engine层Framework层

分别对应:

  • Framework层:用Dart实现的上层UI SDK。
  • Engine层:渲染引擎层。
  • Embedder层:操作系统底层适配层。
  1. Framework 层:Dart实现的上层UI SDK 实现了:Animation(动画)、Painting(图形绘制)Gestures(手势操作)等功能,并包装成对应的 api 提供给上层开发者调用。为了保证Flutter所绘制的控件与原生控件风格类似,Flutter封装了Material(对应Android)、Cupertino(对应iOS)风格的UI组件库,供开发者直接使用。

  2. Engine 层:Skia渲染 + DartVM 引擎
    这层主要包含三块:Skia、Dart、Text。

    • Skia 是渲染引擎,为 Framework 层提供“底层渲染”能力。
    • Dart 是 Dart 运行时引擎,为 Framework 层提供运行时调用Dart和渲染能力。
    • Text 是文字排版,为 Framework 层提供视图排版能力。
  3. Embedder 层:操作系统适配
    对不同平台操作系统的适配,包括一些配置:surface、线程、插件等特性。 由于Flutter相关特性并不多,因此对不同平台操作系统的适配成本很低。

3.执行流程

从结构上看,Flutter渲染由UI Thread与GPU Thread相互配合完成。

1)UI Thread

对应图中1-5,执行Dart VM中的Dart代码(包含应用程序和Flutter框架代码),主要负责Widget Tree、Element Tree、RenderObject Tree的构建,布局、以及绘制生成绘制指令,生成Layer Tree(保存绘制指令)等工作。

2)GPU Thread

对应图中6-7,执行Flutter引擎中图形相关代码(Skia),这个线程通过与GPU通信,获取Layer Tree并执行栅格化以及合成上屏等操作,将Layer Tree显示在屏幕上。

1
2
注:图层树(Layer Tree)是Flutter组织绘制指令的方式, 
类似于Android Rendering里的View DisplayList,都是组织绘制指令的一种方式。

UI Thread与GPU Thread属于生产者和消费者的角色。

Flutter 渲染工作流水线

渲染原理解释了 flutter 是如何跨平台的

渲染过程,UI线程完成布局、绘制操作,生成Layer Tree; GPU线程执行合成并光栅化后交给GPU来处理,其中几个关键步骤:

  • Animate: 遍历_transientCallbacks,执行动画回调方法;
  • Build: 对于dirty的元素会执行build构造,没有dirty元素则不会执行,对应于buildScope()
  • Layout: 计算渲染对象的大小和位置,对应于flushLayout(),这个过程可能会嵌套再调用build操作;
  • Compositing bits: 更新具有脏合成位的任何渲染对象, 对应于flushCompositingBits();
  • Paint: 将绘制命令记录到Layer, 对应于flushPaint();
  • Compositing: 将Compositing bits发送给GPU, 对应于compositeFrame();

GPU线程通过skia向GPU硬件绘制一帧的数据,GPU将帧信息保存到FrameBuffer里面, 然后视频控制器会根据VSync信号,从FrameBuffer取帧数据传递给显示器,从而显示出最终的画面。(Vulkan,OpenGL,Metal)

4.渲染策略

在Flutter中,Everything is widget。所有 Widget 会组成 Widget Tree 。
界面更新时,会更新 Widget Tree ,再更新 Element Tree ,最后更新 RenderObjectTree 。

分为4个阶段,分别是:

1
2
布局阶段 => 绘制阶段 => 合成阶段 => 渲染阶段  
(Layout => Paint => Composite => Rasterize)

  1. 布局(Layout)

Flutter采用 “深度优先” 机制遍历Widget Tree。 为了防止孩子节点的变化,导致整个 Widget Tree 重新布局。 Flutter加入了 “布局边界” 机制(Relayout Boundary) 布局完成后,树上每个节点都确定了“尺寸大小”和“位置”。

  1. 绘制(Paint)

布局完成后,确定了树上的控件的“尺寸”与“位置”。 接下来是绘制阶段。

和布局类似,Flutter也是采用 “深度优先” 机制遍历渲染树。 先绘制自身,再绘制子节点。 为了解决绘制覆盖问题,Flutter采用了也是和布局阶段相似的策略: 重绘边界 机制(Repaint Boundary)。 其实,本质上就是加个新的图层,避免在同一图层重绘产生影响。 典型的例子是,ScrollView。 一旦设置好重绘边界,滚动时,只会重绘ScollView中的视图内容,而其他部分不用重新绘制。

  1. 合成(Composite) 由于绘制出来的渲染树,会有很多层,同步多层渲染会出现性能问题。
    因此,Flutter会在渲染前,将多个渲染树图层进行合成。
    根据多层渲染树的大小、层级、透明度等计算后,
    合成为最终“简化版”的渲染树,以提高下一步的渲染效率。

  2. 渲染(Rasterize) 将处理过的“简化版”渲染树,交给Skia引擎转换成“二维图像数据”。

然后 Skia 把计算好的图形数据,通过 OpenGL 接口交给 GPU 渲染,走 GPU 工作流水线: 顶点着色器 => 形状装配 => 几何着色器 => 光栅化 => 片段着色器 => 测试与混合
然后,GPU工作流水线六阶段完成。最终,展示到终端屏幕上。
当然这只是一个垂直同步信号(VSync)的过程。(按60fps算,一秒需要60个VSync才不会感到卡顿。)

5.工程实例讲解

5.1.常用组件

5.2特性展示
  • 4.1 hotReload
  • 4.2 Android/iOS style

参考资料
Flutter Samples
了解Flutter渲染机制
iOS 浅谈GPU及“App渲染流程”
Flutter 究竟是如何渲染的
Flutter渲染机制—GPU线程
谈一谈Flutter外接纹理
Flutter初始化流程
深入了解Flutter界面开发
深入理解flutter的编译原理与优化
深入理解Flutter Platform Channel
Flutter快速上车之Widget
Flutter 工作原理
Flutter 中文文档
Flutter 架构概览
Flutter 文档
Dart

关于转载

知识共享许可协议

本作品采用知识共享署名 4.0 国际许可协议 进行许可。 转载时请注明原文链接。图片在使用时请保留图片中的全部内容,可适当缩放并在引用处附上图片所在的文章链接。