美文网首页
Flutter性能优化实践 2023-08-31 周四

Flutter性能优化实践 2023-08-31 周四

作者: 勇往直前888 | 来源:发表于2023-08-30 21:11 被阅读0次

简介

APP匆忙上线之后,就提出了性能优化的需求。
一路走来,还是有了一点点进步。

图片

性能优化,图片是绕不开的话题。
图片分为本地图片和网络图片两种。
一般本地图片不存在性能问题,而网络图片存在性能和体验等方面的制约。

图片组件

image.png
  • 以上构造方法,我们用得多的是Image.asset,本地资源图片,没什么好选的,就用这个。并且本地图片除了占包大小,在性能上也是无可挑剔,不需要考虑。

  • Image.memory在很少的场合用,比如登录注册过程的验证码图片。这个也不存在性能问题。

  • 参考文章: Flutter中的 Image图片组件的详解

网络图片

使用历史

  • 一般网络图片的显示,有下面三种选择:
企业微信截图_8f7f4de3-0540-4441-bc19-e4d9173f7da2.png

参考文章: Flutter - 加载网络图片的几种方式

  • 一开始我们用的是Image.network

  • 后来,需要占位图,用过一段时间的FadeInImage;

  • 再后来,要加强体验,想到图片缓存,找到了插件CachedNetworkImage;现在基本上用这个。

缩略图

阿里CDN服务器提供缩略图服务,网络图片尺寸变小,流量节省,性能也会提升。当然,相应的,图片清晰度会下降,体验会降低。这个就需要一个平衡。
我们的首页图片非常多,就用到了这个服务。用起来也非常简单,按照规则,修改图片的url就可以了。

/// 拼接参数,进行图片缩放
/// w和h要一样大
/// w和h要整数
/// w和h只能整10和整100
/// w和h不能根据屏幕自适应(不能保证整10整100)
/// 目前只有alicdn才能这么做;比如微店的geilicdn就不行
String url = item['goodsImg'] ?? '';
if (url.contains('alicdn')) {
  int w = 400;
  int h = 400;
  url = '${url}_${w}x$h.jpg';
}

缓存管理

  • Flutter遇到null或者数组越界等一般不会崩溃,而是直接白屏;这个很好,白屏比崩溃好多了。

  • Flutter的内存目前大约2G左右,一旦内存占用过大,程序退出,现象和崩溃差不多。

  • 一开始CachedNetworkImage没有提供缓存管理,导致内存占用过大,程序退出。

  • 引入flutter_cache_manager之后就好多了。

  • 再后来,发现flutter_cache_manager到达临界值的时候,切换非常慢。所以,根据不同的页面和使用场景,提供不同的配置。

class CustomCacheManager {
  static const key = 'image_cache_key';
  static CacheManager instance = CacheManager(
    Config(
      key,
      stalePeriod: const Duration(days: 7),
      maxNrOfCacheObjects: 100,
    ),
  );

  /// 首页购物时报采用单独的缓存
  static const homeRealTimeKey = 'homeRealTimeKey';
  static CacheManager homeRealTimeInstance = CacheManager(
    Config(
      homeRealTimeKey,
      stalePeriod: const Duration(hours: 2),
      maxNrOfCacheObjects: 200,
    ),
  );

  /// 首页购物时报采用单独的缓存
  static const goodsDetailKey = 'goodsDetailKey';
  static CacheManager goodsDetailInstance = CacheManager(
    Config(
      goodsDetailKey,
      stalePeriod: const Duration(hours: 2),
      maxNrOfCacheObjects: 200,
    ),
  );
}

sliver家族

  • 原先我们很多页面是通过SingleChildScrollView或者ListView来实现的;后来反馈说滑动时卡顿明显。
    `

  • 后来,这几个页面都换成了CustomScrollView, 使用了sliver家族组件,卡顿问题好了很多。

  • sliver家族也成了性能优化的第一选择。现在,但凡需要解决卡顿问题的页面,先用上CustomScrollView试试再说。

  • 参考文章:干货 | Flutter控件CustomScrollView原理解析及应用实践

ListView嵌套

  • 表格套表格的时候怎么办?比如我们的购物车,第一层是订单列表,可滑动,这个理所当然。

  • 每个订单还包含若干个商品。这个我们定义为不可滑动。一般的代码如下:

ListView(
  shrinkWrap: true,
  physics: const NeverScrollableScrollPhysics(),
  children: pis.map((e) => _piCell(get, e)).toList(),
)
  • 优化的时候,第一时间将最外层的ListView替换成了CustomScrollView,但是以往屡试不爽的绝招不管用了,仍然卡顿。

  • 后来,看到了上面不可滑动的表格。有了个想法:“表格不可滑动,并且也没有用到构建函数,这和Column有什么区别?”

  • 抱着试试看的想法,把上面的不可滑动表格用Column来替换,还真解决了困扰已久的卡顿问题。

Column(
  children: pis.map((e) => _piCell(get, e)).toList(),
)
  • 原因不清楚,并且这样的例子只有一个,所以不好下结论。估计shrinkWrap: true, physics: const NeverScrollableScrollPhysics(),这个组合的ListView确实性能不好。

Raster

  • 翻译是光栅,据说跟GPU有关,也就是画图。就是flutter engine层的画图操作。如果这个耗时过大,和原生的离屏渲染有点像。这里是调用了saveLayer()函数,比较耗性能。
    这方面考虑的因素是圆角,透明度,阴影。对应的组件是Clip, Opacity,shadows属性
image.png

这里的意思是默认三个钩都打上,也就是3个方面都检测。然后一个一个去掉,看性能是否有改善。这样就能反推出影响性能的到底是Clip, Opacity,shadows中的哪一个。

耗时函数

  • 耗时函数有时候也会引起性能问题。比如build方法前面先来一些耗时计算,然后再返回Widget,那么就会引起Performancebuild阶段过长。

  • 打开DevTools

企业微信截图_591e924f-a5ff-414c-973e-cc12eeebe594.png
  • 切换到CPU Profiler选项卡
企业微信截图_5cf99c76-b7fe-4aa2-bfd8-0604948cde22.png
  • 三步操作,等待DevTools记录函数操作时间

    企业微信截图_98031bf4-8f10-4b1c-a3e2-a26c31a887fe.png
  • 过滤系统调用

企业微信截图_a355e5fd-ef69-4bfb-8a45-b6d077b24e39.png
  • 观察列表
企业微信截图_038a7af8-b987-42d9-9e92-b023d8204115.png
  • 查看代码
    函数名称,文件名称,所在行数都有了,到对应的代码文件看看
@override
  Map<String, Color?> get lightInfo => {
        PandaColorSring.coltheme: const Color(0xff2865ff),
        PandaColorSring.colffffff: const Color(0xffffffff),
        PandaColorSring.col000000: const Color(0xff000000),
// ... ...
}

这只是一个数组,存放了所有light模式下的颜色。对应的,还有darkInfo,存放了所有dark模式下的颜色定义。

  • 把这个数组情况,再连接上Performance,卡顿情况立马好转,说明卡顿的原因确实是这里。

  • 如何修改?
    上面的两个Map都用了get关键字。这个其实是函数。每次访问到的时候,都会执行一遍。好处是每次都刷新,坏处当然是耗性能。去掉get关键字,作为静态的Map,性能就好了,卡顿明显减少。

@override
  Map<String, Color?> lightInfo = {
        PandaColorSring.coltheme: const Color(0xff2865ff),
        PandaColorSring.colffffff: const Color(0xffffffff),
        PandaColorSring.col000000: const Color(0xff000000),
// ... ...
}

这样改会有警告,集成重写,需要get关键字。所以可以考虑去掉继承,重新整理一下。

暗黑模式检查

  • GetX中,检查是否暗黑模式的函数耗时严重
Get.isDarkMode
  • 一开始,每次取颜色就检查一次是否暗黑模式,导致性能影响较大

  • 为了提升性能,将状态保存在一个静态变量中,只有在切换模式的时候,才修改静态变量。
    这样做,功能不变,但是性能提升明显。

  /// 记忆状态,默认不是暗黑模式
  static bool isDarkMode = false;

  /// 在需要的时候检查状态,更新静态变量
  static checkDarkMode() {
    String type = LocalStorageUtil.getThemeType();
    if (type == 'auto') {
      isDarkMode =
          (MediaQuery.of(Get.context!).platformBrightness == Brightness.dark);
    } else {
      isDarkMode = Get.isDarkMode;
    }
  }

  /// 对外接口,直接返回静态变量,提升性能
  static bool isdark() {
    return isDarkMode;
  }
  • checkDarkMode()只要跟随模式变化函数就好了。模式改变,更新一下静态变量
  static updateTheme() {
    /// 这里需要delay,不能直接调用Get.forceAppUpdate();
    Future.delayed(const Duration(milliseconds: 200), (() {
      Get.forceAppUpdate();

      /// 状态更新后,检查模式,更新静态变量
      checkDarkMode();
    }));
  }

性能分析

  • 有好几次,产品或者视觉说某某页面卡顿,但是我自己试着滑动了一下,感觉还好啊。到底卡不卡呢?该怎么判断?

  • FlutterPerformance选项卡,就是干这个的。简单理解,可以认为,蓝色柱子没事,一旦出现红色柱子,那就是有性能问题,那就是卡。

  • 检查性能的时候,需要讲模式设置为profile。将那几个我自己认为还可以的页面跑了一下,确实有红色柱子时不时冒出来,看来确实“卡”,只是我没有感觉到而已。

image.png

相关文章

网友评论

      本文标题:Flutter性能优化实践 2023-08-31 周四

      本文链接:https://www.haomeiwen.com/subject/ndoomdtx.html