打开 Debug flag debugDisableClipLayers
和debugDisablePhysicalShapeLayers
重新检查视图,可以发现部分 ClipRectLayer 是因为图片内容超出视图边界产生,部分 ClipRRectLayer 是因为卡片 Widget 圆角设置以及基于外接纹理的图片控件里设置了 ClipRRect 设置(即便 radius 为0也会设置)
理解原理后,我们对闲鱼图片控件新增参数,支持图片内容圆角设置和图片内容宽高裁剪,使 native 层生成的 Bitmap 已经满足圆角和宽高比要求。同时修复 radius 为0也会设置 ClipRRect 的问题。优化后的 Timeline 图如下:
4.1.4 其他优化建议
flutter 性能优化相关的优秀文章很多,本文不再对类似的排查和优化手段做赘述,这里做下简单汇总:
widget build 优化
setState 状态刷新位置尽量放置于视图树的低层级
Provider 中获取 Model 的方式会影响刷新范围。推荐使用 Selector 或 Consumer 来获取祖先 Model,以维持最小刷新范围
对于长列表,避免使用 ListView 构造函数,推荐使用 ListView.builder 构造函数
reducer 中,state 对象中的视图数据真正发生变化的时候,新建 state 对象
主 isolate 优化
减少或延迟 widget build 中非视图逻辑,如曝光埋点延迟到滑动停止聚合触发
列表 Item 高度可知的情况下,推荐设置 itemExtent,减少滑动中频繁计算列表高度
使用 const 修饰无需变更的 widget 或普通对象
使用 AnimatedBuilder 时,避免在不依赖于动画的 widget 的构造方法中构建 widget 树。动画的每次变动都会重建这个 widget 树。而应该构建子树的那一部分,并将其作为 child 传递给 AnimatedBuilder
避免在动画中剪裁。如果可能,请在动画开始之前预先剪切图像
Render 线程优化
对于频繁更新的控件(如动画),使用 RepaintBoundary 隔离它,创建单独 layer 减少重绘区域
使用图片替换半透明效果
减少 saveLayer(ShaderMask、ColorFilter、Text Overflow)、clipPath的使用,提升 render 线程性能
避免使用 Opacity widget,尤其是在动画中避免使用。请用 AnimatedOpacity 或 FadeInImage 进行代替
避免使用带换行符的长文本
工具推荐
官方 DevTools 工具
利用 Debug flags 排查问题(推荐 Flutter Performance 分析工具简介)
善于利用框架日志,如 fish-redux 性能日志
4.2 列表 element 复用优化
flutter 列表控件划分为可视区域和 Cache 区域,往下滑动时 element 从底部被创建进入底部 Cache 区域后,再进入可视区域,再进去顶部 Cache 区域,最后被销毁。往上滑动逻辑类似。在不使用 keepAlive 的情况下,来回滑动,曾经创建过的 element 需要重新创建。而在我们的业务中,列表 item Widget 结构是接近的,此时如果能根据类型复用 element,就能一定程度的提升性能。
列表控件源码见 sliver_list.dart 中 RenderSliverList.performLayout element 缓存在 _childElements 数组中,以 index 为索引。源码见 sliver.dart 若 item Widget 结构差异很大,即便复用了 element,Element.updateChild 方法内部最终还是执行了 inflateWidget 方法,对于性能提升就没什么价值了