对 Flutter 的一些看法
阅读原文时间:2023年07月08日阅读:2

Flutter 发布的时候可谓很轰动,相对于 RN 或 PhoneGap 们,它给出了另外一种跨平台方案,更像是 APP 版的 Unity,而且使用现代的声明式 UI,据说能媲美原生性能。很吸引人,所以今天特地了解了一下。

Flutter 自己实现了布局和 2D 图形引擎,不依赖平台提供。实际体验来看,在安卓平台完全和原生性能相当,在 iOS 平台与原生相比还是稍稍能感受到一些不同,主要体现在滚动和动画方面。相信大家都听说过Flutter对应安卓开发意味着什么,网上有很多关于安卓的讨论。本文将以 iOS 开发者的视角来看待 Flutter。

在 iOS 平台,利用高效的 CoreAnimation 和独立进程渲染服务来呈现内容,这是构建性能卓越的原生应用的软件基础。我比较关心性能,一个新技术的出现,如果使用户体验开了倒车,那这门新技术普及的可能就非常小了。所以我们先来看看 Flutter 是如何呈现内容的:

Flutter engine 提供了图形服务、文件 IO 以及事件等核心功能。

由上图可以看出绘制管线主要发生在 UI 任务运行器(UI Task Runner)和GPU任务运行器(GPU Task Runner)上,这2个运行器通常分别跑在各自的线程上。

下面是官方文档的UI任务运行器解释:

  • 根 Isolate 必须告诉引擎需要渲染帧。

  • 引擎将询问平台应该在下一个 vsync 上通知它。

  • 该平台等待下一个 vsync。

  • 在 vsync 上,引擎将唤醒 Dart 代码并执行以下操作

  • 更新动画插值器。

  • 在布局阶段重建应用程序中的 widgets。

  • 布置新构造的 widgets 并将它们绘制成立即提交给引擎的图层树。这里没有任何实际的栅格化; 只有对需要绘制的内容的描述是作为绘制阶段的一部分构建的。

  • 在屏幕上构造或更新包含有关 widgets 的语义信息的节点树。这用于更新平台特定的辅助功能组件。

UI任务运行器主要处理图层布局相关,调用 dramFrame 重建窗口图层树中的所有脏元素等,这里的过程和 CALayer 的 drawInContext 非常相似。

而 Skia 图形库和 TextLayout 等发生在 GPU 任务运行器里,GPU 任务运行器会负责一些 GPU 操作相关的工作,类似于 iOS 平台的独立渲染服务。

我们和 UIKit 比较,Flutter 没有像 UILabel 那样无法在后台线程排版的问题,整体架构也是面向高性能 UI 而构建的,那么为什么还有很多性能差异呢。其实也不奇怪,抛开 VM 和原生机器码的性能差异,苹果在优化方面是精益求精的和有平台针对性的,CoreAnimation 的多进程架构植入 OS 享受着很高的调度特权,Flutter 可能永远也不会比 CoreAnimation 性能更好。但 Flutter 是为跨平台而生的,而且尚处于 Bate 阶段,运行时 VM 以及图形渲染和动画基础结构都有非常多的性能优化空间,如果开发者参与度高或者 Google 开挂扶持,相信改善会非常快。

我们再来看看Flutter的整体架构:

主要分为 Dart 的 Framwork 层、C++ 的引擎层以及平台相关的嵌入层。可见理论上只要适配嵌入层就可以让 Flutter 在任何系统上运行,很期待未来 Flutter 在例如智能家居等嵌入式设备上的表现。

实际测试发现,在 iOS 平台实际启动时还是会先跑完整的原生应用冷启动过程,完了再去启动 Flutter 系统包括注册 Dart VM 等,还是比较耗时的,播放完开机画后会有一段时间的黑屏,我想应该可以通过原生壳来掩盖黑屏改善体验,当然也期待能优化后能让冷启动过程速度更快一些。

总结来看,还是相当看好的,开发体验做的真的很不错,对于一个全新的跨平台方案,性能问题(用户体验)解决后,其他都不是问题。(完)