深入了解Flutter的高性能图形渲染(一)

1,803 阅读3分钟

flutter如何拥有媲美原生应用的性能

一般来说,我们需要在开发者的效率和程序的效率之间做一个取舍,对于跨平台的项目,一份代码,编译成两个平台的APP,在提升开发者效率的同时,可能都会牺牲一些程序的效率。

我们以下面这幅图为例:

首先看中间的Android框架,一个Android的原生APP 在绘图的时候,首先要调用Android框架的Java代码,然后再调用Skia( C C++)的绘图引擎代码,最后生成CPU或GPU的指令完成绘图。

这里可以比较明显的看到,flutter和RN 以及其他跨平台的语言相比,有一个优势就是不需要调用Android框架的Java代码,而是在调用dart代码之后,也直接调用了Skia去完成绘图。

所以,上图可以看出Flutter框架中的Dart代码完全取代了Android框架中的Java代码,所以当dart代码的效率优于Java代码时,flutter就会拥有可以媲美原生APP的性能。

Skia

Skia也是Google维护的一个开源的project,他作为一个图形引擎被广泛应用于Chrome,Android,Firefox,sublime,Adobe等许多其他软件当中。

Skia作为安卓操作系统的一部分,只有当安卓操作系统升级时才会升级 而对于flutter,Skia是Flutter SDK的一部分,只要Flutter SDK升级,Skia就会升级

所以,当flutter有性能上的改进,或者Skia有性能上的改进,Flutter APP 很快就会得到性能上的改进,并且不需要进行系统升级。而对于安卓原生应用,有些老旧的安卓设备可能不确定什么时候才能升级安卓操作系统,或者是否支持安卓系统的升级

什么是应用性能

主要关注下渲染的pipeline的性能 下图 是两种操作系统(左边安卓 右边iOS )的渲染过程

左边安卓的渲染过程,可以打开Android systrace工具查看主干线的每60毫秒之下,出现的I/Choreographer之下,动画的演变、测量布局、绘制等步骤。

所以我们最终所说的性能,就是我们的UI 系统能够多么有效的每16毫秒运行着所有这些步骤。

而flutter的渲染过程与安卓和iOS类似

如何使用flutter自带的工具观看整个渲染过程

这里要注意一点,flutter中debug模式,和最终的生产模式,有很不同的性能特点,所以我们在做这种帧时间的测量之前,我们要使用profile mode来做这种测试。 以profile mode运行的两种方式:

1.Android studio中 在菜单栏中点击 Run->Profile

2.command line

$ flutter run --profile

把我们的测试代码运行起来之后呢,在Android studio的右侧菜单栏中有一个非常有用的工具叫 Flutter Inspector 下图就是是整个widget树的结构:

然后点击菜单栏中的 show performance overlay:

就可以在模拟器上看到 每一帧渲染用了多少时间,因为我这里没有安卓的真机 ,所以是用了debug模式运行在了模拟器上,大家有条件可以运行profile mode在真机上,然后关注一下这个具体的时间。

或者点击右侧菜单栏中的Flutter Performance直接在Android studio中查看

渲染责任部件

Android的渲染部件是view,iOS则是UIView, 而flutter中可以和iOS,Android相对应的渲染责任部件是RenderObjectRenderObject是一个有长生命周期 有状态的一个UI 单位(由于flutter特殊的组合结构,所以RenderObject的API中类方法只有五十个左右,比iOS和Android的责任部件中的类方法少很多)。

但是和iOS与Android不同的是,flutter有一个非常不同的布局系统。可以看到他只有一个布局函数,然后所有的布局功能,也是通过执行performLayout来实现。layout之上还有一个步骤是build

build阶段

上面提到的RenderObject并不负责build过程,那么又是什么去负责呢?

比如 我们把上面代码中的Text部分的property做一下修改(Text('B')),这个更新的过程是怎么样的呢?

我们知道flutter中的树是不可改变的,包括树之间的父子结构,都是不可改变的,所以我们要改变这个树的话,我们只能把之前的树扔掉,重新创造一个树,这就是build过程的开始

然后开始对上一帧的element tree 进行遍历,具体如何遍历呢?主要就是element.updateChild()这个方法,他的逻辑有下面几个步骤: 1、查看一下子节点的类型跟刚才的是否一样, 2、如果一样就对这个子节点做更新;具体更新方法: 看element类型 如果是Component类型,就对这个element的statefullwidget和statelesswidget做更新,调用build
如此遍历所有节点 3、如果不一样,就把这个子树扔掉,创建一个新的子树

对于上面这个简单的例子,遍历之后发现只有最后一个子树被标脏,因为我们改变了他的property

了解了build的过程,我们就可以对于build过程,提高效率了

如何用工具检测build过程

flutter给我们提供了一些工具,使用这些工具我们可以看到有哪些节点被重构了,为什么被重构等。这里需要注意这些都是调试的工具而不是测量工具,运行过程中需要处理和储存大量数据,所以他们会影响运行的速度,所以这些工具需要在debug模式运行。

首先看下flutter提供的Observatory,Observatory是用于分析和调试Dart代码的工具,因为Flutter自带Dart VM,所以也可以用Observatory。 打开方式:

  • 命令行运行 $ flutter run
  • 会看到最下面有一行 An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:59193/r4t5XNurW5c=/
  • 浏览器打开http://127.0.0.1:59193/r4t5XNurW5c=/