Flutter 应用性能优化最佳实践
通常来说,Flutter 技术构建的应用程序在默认情况下都是高性能的。所以你只需要避开常见的陷阱,就可以获得优异的性能,而不需要使用复杂的分析工具对细节做优化。这些最佳建议将帮助你编写性能最佳的 Flutter 应用程序。
最佳实践
如何设计一个能最有效地渲染页面的 Flutter 应用程序?特别是如何确保底层框架生成的绘图代码尽可能高效?这里有几件需要你在设计应用时考虑的事情:
控制 build() 方法的耗时
避免在
build()
方法中进行重复且耗时的工作,因为当父 widget 重建时,子 Wdiget 的build()
方法会被频繁地调用。避免在一个超长的
build()
方法中返回一个过于庞大的 widget。把他们分拆成不同的 widget,并进行封装,另外他们要这样改变:当在 State 上调用
setState()
时,所有后代 widget 都将重建。因此,将setState()
的调用转移到其 UI 实际需要更改的 widget 子树部分。如果改变的部分仅包含在 widget 树的一小部分中,请避免在 widget 树的更高层级中调用setState()
。当重新遇到与前一帧相同的子 widget 实例时,将停止遍历。这种技术在框架内部大量使用,用于优化动画不影响子树的动画。请参阅 [TransitionBuilder][] 模式和使用此原则的 [SlideTransition][],以避免在动画过程中重建其后代 widget。
另见:
Statefulwidget
API 文档的 Performance considerations 部分。
仅当需要的时候才应用效果
由于代价很大,请谨慎使用效果。一些效果的背后调用了性能代价很大的 saveLayer()
方法。
为什么 saveLayer 代价很大?
调用 saveLayer()
会开辟一片离屏缓冲区。将内容绘制到离屏缓冲区可能会触发渲染目标切换,这些切换在较早期的 GPU 中特别慢。
一些在使用效果时的通用规则:
能不用
Opacity
widget,就尽量不要用。有关将透明度直接应用于图像的示例,请参见 Transparent image,这比使用 Opacity widget 更快。Clipping 不会调用
saveLayer()
(除非明确使用Clip.antiAliasWithSaveLayer
),因此这些操作没有 Opacity 那么耗时,但仍然很耗时,所以请谨慎使用。
其他会触发 saveLayer()
的 widget,可能也会代价高昂。
ShaderMask
ColorFilter
[Chip][]—当
disabledColorAlpha != 0xff
的时候,会调用saveLayer()
Text
—might cause call tosaveLayer()
if there’s anoverflowShader
[Text][]— 当有 overflowShader
时,会调用saveLayer()
避免调用 saveLayer()
的方式:
要在图像中实现淡入淡出,请考虑使用 FadeInImage widget,该 widget 使用 GPU 的片段着色器应用渐变不透明度。了解更多详情,请参见
Opacity
文档。要创建带圆角的矩形,而不是应用剪切矩形,请考虑使用很多 widget 都提供的
borderRadius
属性。
对列表和网格列表懒加载
在构建大型网格或列表时,使用带有回调的惰性方法。这样,只有屏幕的可见部分是在开始时构建的。
请参阅:
来自社区的 AbdulRahman AlHamali 撰写的Creating a ListView that loads one page at a time
Listview.builder
API
在 16ms 内渲染完成每一帧
由于构建和渲染有两个独立的线程,因此构建时间为 16ms,60Hz 显示器上渲染时间为 16ms。如果需要考虑延迟,就要在 16ms 或更短 的时间内构建和显示帧。请注意,这意味着构建需要少于 8ms,渲染也需要少于 8ms,总计 16ms 或更短。如果需要考虑丢帧(jankyness),那么每个构建和渲染阶段的 16ms 都可以。
如果在 profile 构建 状态下,每一帧渲染时间低于 16ms,你可能不必担心性能问题以及一些性能陷阱,但仍然应该致力于尽可能快地渲染每一帧。为什么?
将帧渲染时间降低到 16ms 以下可能在视觉上看不出来什么变化,但可以延长电池寿命以及避免发热问题。
可能在你当前测试设备上运行良好,但请考虑在应用所支持的最低端设备上的情况。
当 120fps 的设备普及之后,便需要在 8ms 之内完成每一帧的渲染来保证流畅平滑的体验。
如果你想弄明白为什么 60fps 会带来平滑的视觉体验,请看视频 Why 60fps?
陷阱
如果你需要调整应用程序的性能,或者 UI 顺畅度没达到你的预期,那么 IDE 的 Flutter plugin 可以提供帮助。在 Flutter Performance 窗口中,勾选 Show widget rebuild information 复选框。此功能可帮助你检测帧的渲染和显示时间是否超过 16ms。在可能的情况下,插件提供指向相关提示的链接。
以下行为可能会对您应用的性能产生负面影响。
避免使用
Opacity
widget,尤其是在动画中避免使用。请用AnimatedOpacity
或FadeInImage
进行代替。更多信息,请参阅Performance considerations for opacity animation。使用 AnimatedBuilder 时,请避免在不依赖于动画的 widget 的构造方法中构建 widget 树。动画的每次变动都会重建这个 widget 树。而应该构建子树的那一部分,并将其作为 child 传递给 AnimatedBuilder。更多内容,请查看 这个文档。
避免在动画中剪裁。如果可能,请在动画开始之前预先剪切图像。
如果大多数 children widget 在屏幕上不可见,请避免使用返回具体列表的构造函数(例如
Column()
或ListView()
),以避免构建成本。
Resources
要了解更多性能信息,请参见以下资源:
AnimatedBuilder API 页面的 Performance optimizations 部分;
Opacity API 页面的 Performance considerations for opacity animation 部分;
ListView API 页面中 Child elements’ lifecycle,以及如何高效加载元素;
Statefulwidget 的 Performance considerations API 文档。