Flutter 区别于其他方案的关键技术有哪些?

前言

Flutter 是构建 Google 物联网操作系统 Fuchsia 的 SDK,主打跨平台、高保真、高性能。开发者可以通过 Dart 语言开发 App,一套代码可以同时运行在 iOS 和 Android 平台。 Flutter 使用 Native 引擎渲染视图,并提供了丰富的组件和接口,这无疑为开发者和用户都提供了良好的体验。

接下来从原理层面看看 Flutter 有哪些关键的技术

(一)Flutter 是如何运转的

与其他大多数移动框架不同,Flutter 重写了一整套包括 底层渲染逻辑上层开发语言 的完整解决方案。这样不仅保证视图渲染在 Android 和 iOS 上的高度一致性(即高保真),在代码执行效率和渲染性能上也可以媲美原生 App 的体验(即高性能)

React Native 之类的框架,只是通过 JS 虚拟机扩展调用系统组件,由 Android 和 iOS 系统进行组件的渲染;而 Flutter 则是自己完成了组件渲染的闭环。

Flutter 区别于其他方案的关键技术有哪些?_第1张图片
Flutter 绘制原理

Flutter 关注如何尽可能快地在两个硬件时钟的 VSync 信号之间计算并合成视图数据,然后通过 Skia 交给 GPU 渲染:UI 线程使用 Dart 来构建视图结构数据,这些数据会在 GPU 线程进行图层合成,随后交给 Skia 引擎加工成 GPU 数据,而这些数据会通过 OpenGL 最终提供给 GPU 渲染。

(1)Skia

Skia 是 Flutter 的底层图像渲染引擎。是 Flutter 向 GPU 提供视图数据的帮手。

Skia 是一款用 C++ 开发的、性能彪悍的 2D 图像绘制引擎,其前身是一个向量绘图软件。2005 年被 Google 公司收购后,因为其出色的绘制表现被广泛应用在 Chrome 和 Android 等核心产品上。Skia 在图形转换、文字渲染、位图渲染方面都表现卓越,并提供了开发者友好的 API。

目前,Skia 已然是 Android 官方的图像渲染引擎了,因此 Flutter Android SDK 无需内嵌 Skia 引擎就可以获得天然的 Skia 支持;而对于 iOS 平台来说,由于 Skia 是跨平台的,因此它作为 Flutter iOS 渲染引擎被嵌入到 Flutter 的 iOS SDK 中,替代了 iOS 闭源的 Core Graphics/Core Animation/Core Text,这也正是 Flutter iOS SDK 打包的 App 包体积比 Android 要大一些的原因。

底层渲染能力统一了,上层开发接口和功能体验也就随即统一了,开发者再也不用操心平台相关的渲染特性了。也就是说,Skia 保证了同一套代码调用在 Android 和 iOS 平台上的渲染效果是完全一致的。

(2)Dart

在 Dart 语言的特点 一文中提到,Dart 语言的特点。
当然,Google 公司选择使用 Dart 作为 Flutter 的开发语言,我想还有其他更有说服力的理由:

  1. Dart 同时支持即时编译 JIT 和事前编译 AOT。在开发期使用 JIT,开发周期异常短,调试方式颠覆常规(支持有状态的热重载);而发布期使用 AOT,本地代码的执行更高效,代码性能和用户体验也更卓越。
  2. Dart 作为一门现代化语言,集百家之长,拥有其他优秀编程语言的诸多特性(比如,完善的包管理机制)。也正是这个原因,Dart 的学习成本并不高,很容易上手。
  3. Dart 避免了抢占式调度和共享内存,可以在没有锁的情况下进行对象分配和垃圾回收,在性能方面表现相当不错。

(二)Flutter 的原理

Flutter 区别于其他方案的关键技术有哪些?_第2张图片
Flutter 架构图

Flutter 架构采用分层设计,从下到上分为三层,依次为:Embedder、Engine、Framework。

  1. Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配;
  2. Engine 层主要包含 Skia、Dart 和 Text,实现了 Flutter 的渲染引擎、文字排版、事件处理和 Dart 运行时等功能。Skia 和 Text 为上层接口提供了调用底层渲染和排版的能力,Dart 则为 Flutter 提供了运行时调用 Dart 和渲染引擎的能力。而 Engine 层的作用,则是将它们组个起来,从它们生成的数据中实现视图渲染。
  3. Framework 层则是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口,Flutter 还基于这些基础能力,根据 Material 和 Cupertino 两种视觉设计风格封装了一套 UI 组件库。
那么,Flutter 是如何工作的呢?以界面渲染过程为例:

页面中的各界面元素(Widget)以树的形式组织,即控件树。Flutter 通过控件树中的每个控件创建不同类型的渲染对象,组成渲染对象树。而渲染对象树在 Flutter 的展示过程分为四个阶段:布局、绘制、合成和渲染。

(1)布局

Flutter 采用深度优先机制遍历渲染对象树,决定渲染对象树中各渲染对象在屏幕上的 位置尺寸。在布局过程作用,渲染对象树中的每个渲染对象都会接收到父对象的布局约束参数,决定自己的大小,然后父对象按照控件逻辑决定各个子对象的位置,完成布局过程。
PS:有点类似 Android View 树中的 Measure 和 Layout

不同于其他,为了防止因子节点发生变化而导致整个控件树重新布局,Flutter 加入了一个机制 --- 布局边界(Relayout Boundry),可以在某些节点自动或手动地设置布局边界,当边界的任何对象发生重新布局时,不会影响边界外的对象,反之亦然。

Flutter 区别于其他方案的关键技术有哪些?_第3张图片
Flutter 布局边界

(2)绘制

布局完成后,渲染对象树中的每个节点都有了明确的尺寸和位置。Flutter 会把所有的渲染对象绘制到不同的图层上。与布局过程一样,绘制过程也是 深度优先遍历,而且总是先绘制自身,再绘制子节点。

以下图为例:节点 1 在绘制完自身后,会再绘制节点 2,然后绘制它的子节点 3、4 和 5,最后绘制节点 6。


Flutter 区别于其他方案的关键技术有哪些?_第4张图片
Flutter 绘制示例

可以看到,由于一些其他原因(比如,视图手动合并)导致 2 的子节点 5 与它的兄弟节点 6 处于了同一层,这样会导致当节点 2 需要重绘的时候,与其无关的节点 6 也会被重绘,带来性能损耗。

为了解决这一问题,Flutter 提出了与布局边界对应的机制 --- 重绘边界(Repaint Boundary)。在重绘边界内,Flutter 会强制切换新的图层,这样就可以避免边界内外的互相影响,避免无关内容置于同一图层引起不必要的重绘。

Flutter 区别于其他方案的关键技术有哪些?_第5张图片
Flutter 重绘边界

重复边界的一个典型场景是 Scrollview。Scrollview 滚动的时候需要刷新视图内容,从而触发内容重绘。而当滚动内容重绘时,一般情况下其他内容是不需要重绘的,这时候重绘边界就派上用场了。

(3)合成和渲染

随着终端设备的页面越来越复杂,Flutter 的渲染树层级通常很多,可能会出现大量渲染内容的重复绘制,所以需要将所有的图层根据大小、层级、透明度等规则计算出最终的显示效果,将相同的图层归类合并,简化渲染树,提高渲染效率。

合成完成后,Flutter 会将几何图层数据交由 Skia 引擎加工成二维图像数据,最终交由 GPU 进行渲染,完成界面的展示。


总结

主要学了,Flutter 区别于其他跨平台方案的核心技术:Skia 和 Dart;
Flutter 的运作机制 ,并以界面渲染过程为例,从布局、绘制、合成和渲染四个阶段学习了 Flutter 的实现原理。

你可能感兴趣的:(Flutter 区别于其他方案的关键技术有哪些?)