在实际的Flutter开发中,能够发现编辑器AS会提示在组件之前加上const关键字,

实测分析Const在Flutter中的性能表现

这是由于Flutter2之后,多了一个linter规则,prefer_const_constructors,官方主张首选运用const来实例化常量结构函数。

实测分析Const在Flutter中的性能表现

那const效果是什么?并且在功能方面对整个app有多大的提升?

一、Const的效果

const 是 constant 的缩写,原意是不变的,不易改变的意思,包括C++、go中都有此关键字,相同的,在Flutter中也是表示不变的意思。具体来看看下面的代码。

Row(
      children: [
        Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
        Text("$_counter")
      ],
    );

这是一个水平布局,内部排列了一个Image和Text,留意这个Text的是有一个动态的值_counter。

为了能够更新_counter,必定要调用setState() 办法。咱们都知道,假如调用setState() ,那么整个Row包括Image和Text都会主动递归重建。每调用一次,父widget和子widget都会重建一次,那么在杂乱的UI和事务场景下,就加深了app的不稳定性。

这就是为什么在开发中,要尽量在小的规模去运用setState,防止不必要的重建任务。为了优化这个问题,官方就更新出了const关键字,被const润饰的widget,就代表永远不会被重建。

比方在上述代码中Image是不可变的,Text是可变的,那么在Image之间加上const润饰,当调用setState() 时,只会更新Text,Image不会被重新构建。

Row(
      children:  [
        const Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
        Text("$_counter")
      ],
    );

二、功能剖析

2.1 widget rebuild状况

DevTools供给了一个查询widget rebuild状况的东西,在 Widget rebuild stats 中勾选 Track widget rebuilds 来查看 widget 的重建信息。重建信息包括 Widget 名字、源码方位、上一帧中重建次数、当时页面中重建次数。

实测分析Const在Flutter中的性能表现

在每个widget之前都有一个小图标,

  • 黄色旋转圆圈 – 重建次数过多
  • 灰色圆圈 – 未重建
  • 灰色旋转圆圈 – 重建

为了进行const比照,咱们以上面代码为例,

Row(
  children:  [
  const Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
  Text("$_counter")
  ],
);

在Image前加上const,Text则不加,当调用setState时,观察两个widget的状况。

实测分析Const在Flutter中的性能表现

清楚的发现,没加const的Image widget前面的圆圈在旋转,则表示Image在重建,且重建次数+1。

2.2 内存占用

关于内存,DevTool相同供给了内存剖析东西Memory,接下来结合事例进行剖析。

在项目中新建两个类,内部不做额外的动作,

void _buildConstObject(){
  const ConstObject();
}
void _buildConstObjectNot(){
  ConstObjectNot();
}

其中ConstObject 加上const润饰,ConstObjectNot则不进行润饰,在触发build时,两个目标一起进行1000次的创立,

void _doBuild(){
  for(var i = 0; i< 1000;i++){
    _buildConstObject();
    _buildConstObjectNot();
  }
}

翻开内存剖析东西,能够发现未加Const润饰的ConstObjectNot创立了1000个目标,所占用内存约16k,而加了const的ConstObject则能够忽略不计。

留意这儿ConstObjectNot和ConstObject内部是没有做任何widget创立的,假如在实际杂乱的项目中,未运用const,内存将成倍增加。

实测分析Const在Flutter中的性能表现

2.3 流畅性

在DevTool中翻开performance overlay, 在app顶部就会呈现功能图层,这两张图表显现的是运用的耗时信息。假如 UI 产生了卡顿(跳帧),这些图表能够协助剖析运用中卡顿,每一张图表都代表当时线程的最近 300 帧体现。

实测分析Const在Flutter中的性能表现

如上图,第一张图归于raster 线程的功能状况即GPU功能,第二张图显现的UI线程功能体现。
当中垂直的绿色条条代表的是当时帧。每一帧都应该在 1/60 秒(大约 16 ms)内创立并显现。假如有一帧超时(任意图画)而无法显现,就导致了卡顿,图表之一就会展示出来一个赤色竖条。假如是在 UI 图表呈现了赤色竖条,则标明 Dart 代码耗费了大量资源。而假如赤色竖条是在 GPU 图表呈现的,意味着场景太杂乱导致无法快速烘托。

为了验证流畅性,咱们开启了一个动画,动画在规定时间内进行重复性的扩大缩小动作,且分为两个场景,一个场景是在所有widget以及目标前加上const润饰,另外一个场景则什么都不做,比照查看每帧的耗时。

class AnLogo extends AnimatedWidget {
  static final _opacityTween = Tween<double>(begin: 0.1, end: 1.0);
  static final _sizeTween = Tween<double>(begin: 0.0, end: 300.0);
  const AnLogo({Key? key, required Animation<double> animation})
  : super(key: key, listenable: animation);
  @override
  Widget build(BuildContext context) {
    Animation<double> animation1 = listenable as Animation<double>;
    return Scaffold(
      appBar: AppBar(
        title: const Text("动画"),
      ),
      body: Center(
        child: Opacity(
          opacity: _opacityTween.evaluate(animation1),
          child: Container(
            margin: const EdgeInsets.symmetric(vertical: 10.0),
            height: _sizeTween.evaluate(animation1),
            width: _sizeTween.evaluate(animation1),
            child: Image.asset("images/ic_1.jpeg"),
          ),
        ),
      ),
    );
  }
}
no const const
no const const
实测分析Const在Flutter中的性能表现
实测分析Const在Flutter中的性能表现

GPU帧率

GPU
no const均匀最大耗时/帧 9.9ms/frame
const均匀最大耗时/帧 7.6ms/frame

UI线程帧率:

UI线程
no const均匀最大耗时/帧 7.8ms/frame
const均匀最大耗时/帧 7.1ms/frame

从试验结果上看,没有加const的GPU帧率均匀最大到达9.9ms/帧,而加了const的GPU帧率比之降低了约2.3ms;UI帧率(CPU)加const与不加const相差不大,约0.7ms。

三、总结

从上面的测试看,不管是内存占用仍是流畅性,添加const润饰的功能都是优于未添加const润饰的功能,const减少了组件的重建以及目标的创立,进行flutter开发时,在合适的时机去运用const以减少不必要的开支。

引荐阅读:

Flutter实战项目开源

Flutter: ‘ const ‘结构函数的功能剖析