「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」。
在上篇文章「iOS 原生渲染与 Flutter 有什么区别 (上)」中,我们了解了渲染原理以及原生渲染的流程,在这篇文章中,我们来聊聊Flutter
是如何渲染,以及前端是如何在iOS上渲染。
Flutter 渲染
Flutter
界面是由 Widget
组成的,所有 Widget
组成 Widget Tree
,界面更新时会更新 Widget Tree
,然后再更新 Element Tree
,最后更新 RenderObject Tree
。
接下来的渲染流程,Flutter 渲染在 Framework
层会有 Build
、Wiget Tree
、Element Tree
、RenderObject Tree
、Layout
、Paint
、Composited Layer
等几个阶段。将 Layer
进行组合,生成纹理,使用 OpenGL
的接口向 GPU 提交渲染内容进行光栅化与合成,是在 Flutter
的 C++
层,使用的是 Skia
库。包括提交到 GPU 进程后,合成计算,显示屏幕的过程和 iOS 原生基本是类似的,因此性能也差不多。 Flutter
在缓冲策略上与 iOS原生稍有不同的是:
Flutter
使用的是 VSync + 三重缓冲 (Triple Buffering)
,在iOS原生
使用的是 VSync + 双重缓冲 (Double Buffering)
。
渲染管道
Flutter
渲染管道涉及到多重步骤:
- 首先得到用户的输入,例如触摸屏幕事件,一些动画可能会随之产生,然后开始构建组件并去渲染它们;
- 渲染可以细分为3个子步骤;
Layout
(布局),它的作用是在屏幕上确定每个组件的大小和位置;Paint
(绘制),它提供一系列方法把组件渲染成用户看到的样子;Composite
(图层合成)它把绘制步骤生成的图层或者纹理堆叠在一起,按照顺序组织它们,以便它们可以高效的在屏幕上进行呈现,图层合成是组件最终呈现在屏幕上之前很关键的也是最后的一个优化步骤;
- 最后是光栅化,它把抽象的表现映射成物理像素显示在屏幕上。
前端 渲染
前端大部分都是使用WebKit
框架下的WebView
,WebView
需要额外解析 HTML + CSS + JavaScript
代码,首次内容加载时, WebView
会比原生渲染慢。
除了首次加载解析要耗时,以及 JavaScript
语言本身解释慢导致的性能问题外,WebView
的渲染进程是单独的,每帧的更新都要通过 IPC 调用 GPU 进程。频繁的 IPC 进程通信也会有性能损耗。
WebView
的单独渲染进程还无法访问 GPU 的 context,这样两个进程就没有办法共享纹理资源。纹理资源无法直接使用 GPU 的 Context 光栅化,那就只能通过 IPC 传给 GPU 进程,这也就导致 GPU 无法发挥自身的性能优势。由于 WebView
的光栅化无法及时同步到 GPU,滑动时容易出现白屏。
写在最后
相比较,不难发现其实Flutter
某些层面上可能表现和原生不相上下,某些层面甚至是超越,相比较而言,由于WebView
渲染层级多,限制多,计算多,所以在性能和速度方面还是要弱于原生和Flutter
。