Qml开发中的性能Tips(翻译文)
来源 https://cloud.tencent.com/developer/article/1468124
1.关于图像性能Tips
1.1 位图格式对比矢量图格式
Qt支持任何标准图像格式,包括PNG和JPEG等位图格式,以及SVG等矢量图形格式。与位图图像相比,渲染SVG图像很慢。
1.2 异步加载大图像
如果同步加载图像,则会阻塞UI界面。在许多情况下,图像不需要立即可见,因此它们可以是延迟加载的。
- 如果不需要立即显示图像,则应在单独的线程中异步加载图像。这可以通过将QML的Image异步(asynchronous)设置为true来完成。这样,用户界面就可以保持响应。
- 请注意,此属性仅对从本地文件系统读取的图像有效。通过网络资源(例如HTTP)加载的图像始终是异步加载的。
1.3 避免调整和缩放
- 调整大小/缩放是QML中非常繁重的操作。使用原始大小的图像,而不是调整大小图像的大小/缩放大小。
1.4 大图像使用sourceSize属性
图像通常是QML用户界面中使用占用最大的内存。
- sourceSize应与大图像一起使用,因为属性设置为加载的图像则存储着实际像素数。
- 如果你有一个很大的图像32642448,但你设置了sourceSize为204153,那么它会缩小并将被存储为204*153的内存。
- 如果图像的实际大小大于sourceSize,则缩小图像。 这样,大图像不会占用超过必要的内存;
- 这对于从外部源加载或由用户提供的内容尤为重要。
- 请注意,动态更改此属性会导致重新加载图像源,甚至可能来自网络,如果它不在内存缓存中。
- 图像在内部进行缓存和共享,因此如果多个图像元素使用相同的源,则只加载图像的一个内存。
1.5 仅在必要时启用Image的smooth属性
启用smooth属性对性能不利。使用自然大小的图像或禁用动画中的平滑(smooth)处理。
- Image的smooth属性可在缩放或转换时平滑处理图像。
- 平滑处理提供更好的视觉质量,但速度较慢。
- 如果图像以其自然大小显示,则Image的smooth没有视觉效果或性能影响。
- 如果您确实需要启用Image的smooth属性,请在动画开始时禁用平滑处理,并在动画结束时重新启用它(仅当图像在屏幕上静止时,缩放瑕疵才可见)。
1.6 避免由多个元素组成图像
由单个图像组成的图像比由多个元素组成图像效率更高。
- 例如,可以使用放置在提供阴影的图像上的矩形来创建具有阴影的图像。
- 提供包括框架和阴影的图像效率更高。
2.关于列表性能Tips
2.1 确保您的数据模型尽可能快
在许多情况下,慢速模型(slow model)实际上是列表滚动性能的瓶颈。请确保数据模型尽可能快。
- 视图被轻弹(拖动)时,必须快速创建代理;
- 例如,在单击委托时仅需要的任何其他功能应由Loader在需要时创建;
- 在委托中将QML的数量保持在最低水平。委托中的元素越少,视图的滚动速度就越快;
- 在列表委托中,仅将QML用于用户界面,并使用C++实现其余部分(例如:数据生成,数据处理)。不要使用JavaScript。
2.2 在ListView/GridView中使用CacheBuffer
在某些情况下,cacheBuffer在改善ListView/GridView性能方面很有用。默认的cacheBuffer为零。
- cacheBuffer属性确定是否在视图的可见区域之外实例化委托(delegate)。
- 请注意,cacheBuffer以像素为单位定义,例如: 如果委托高20像素,则cacheBuffer设置为40(最多2个委托实例),可见区域下方的2个委托实例可以保留在内存中。
- 设置此值可以提高滚动行为的流畅性,但要牺牲额外的内存使用量。数据本身不缓存,但缓存的是实例化委托。
- 对于较短的列表,那么其中每个项都可以缓存。
- 对于较长的列表,cacheBuffer没有带来好处,因为创建条目的速度与快速滚动时没有缓存的速度相同。cacheBuffer只是推迟了问题的发生,也就是说,它只是将委托创建的位置推到列表/网格可见部分的上方/下方。 更多关于cacheBuffer信息请查看:
http://doc.qt.io/qt-5/qml-qtquick-listview.html#cacheBuffer-prop
2.3 避免无用的绘画
你应该防止在同一个区域重复绘画。例如,如果您提供了应用程序的背景,则可以防止QDeclarativeView绘制其窗口背景:
QDeclarativeView window;
window.setAttribute(Qt::WA_OpaquePaintEvent);
window.setAttribute(Qt::WA_NoSystemBackground);
window.viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
window.viewport()->setAttribute(Qt::WA_NoSystemBackground);
此外,考虑使用Item作为根元素而不是Rectangle,以避免多次绘制背景:
- 如果你的根元素是一个Rectangle,就会绘制每个像素,甚至可能是几次。
- 系统QDeclarativeView首先绘制背景,然后绘制所有QML元素。
- 您可能有一个Rectangle作为根元素,并且内部有很多元素,没有不透明度覆盖大部分Rectangle。在这种情况下,系统正在进行无用的绘画。
- 您可以改为使用Item作为根元素,因为它没有视觉外观。
- 如果您需要绘制背景,但是具有覆盖屏幕一部分的静态UI元素,您仍然可以使用Item作为根元素并在这些静态项之间锚定一个Rectangle。这样你就不会做无用的绘画。 更多信息请查看:
http://doc.qt.io/qt-5/qtquick-performance.html#rendering
3.使用动态加载优化性能
如果需要解析大量QML,则QML应用程序会缓慢启动。如果整个应用程序在一个代码量巨大的QML文件中实现,就会发生这种情况。明智地将应用程序划分为逻辑实体,在开始时加载最小QML,然后再使用加载器Loader根据需要加载更多QML。
- Loader控件可用于动态加载和卸载在QML文件中定义的可视QML组件或在QML文件中定义的项/组件。这种动态行为允许开发人员控制应用程序的内存使用和启动速度。
- 将应用程序划分为几个QML文件,以便每个文件包含一个逻辑UI实体。这种装卸方式更容易控制。每个应用程序不应该写一个巨大代码量的QML文件。
- 在应用程序启动时加载绝对最少量的QML,以使您的应用程序尽快启动。在应用程序UI可见后,您可以连接到网络并显示微调器等。
- 如果您的第一个视图非常复杂并且需要加载大量QML,请显示一个启动画面,让用户感觉某些事情正在发生(过渡效果)。
- 您应该只根据需要加载UI片段,例如当用户导航到另一个视图时,但是另一方面,在视图之间导航(切换)可能需要更多的时间。 更多Loader控件信息请查看:
http://doc.qt.io/qt-5/qml-qtquick-loader.html
4.其他QML的一些性能Tips
如果您有一个固定长度的简单列表,您可以尝试使用Flickable+Column+Repeater来优化性能,而不是使用QML的ListView。虽然创建列表会慢一些,但是列表滚动会更流畅。
4.1 在过渡动画中尽可能为屏幕的小区域设置动画
如果您需要在一秒钟内移动3个元素,请尝试每次移动300毫秒。该系统可以计算需要重新绘制的项的边界,并在这些边界内绘制所有内容。
4.2 避免复杂的裁剪
您应该只在真正需要的时候启用裁剪clip功能。默认clip值为false。
- 如果启用了裁剪,则Item将把自己的绘制以及其子项的绘制裁剪到其边界矩形。
4.3 如果从QML文件中去掉注释或空白,是否有助于提高性能?
不是真的。这些文件在启动时被重新处理为二进制内存表示,因此到运行时应该不会有性能差异。您可能很幸运,获得了0.5%的改进,然后只在启动时(QML解析就是在这里完成的),其他地方都没有。
4.4 避免不必要的转换
如果属性的给定值与属性指定的类型不匹配,QML将执行类型转换。这种转换会消耗额外的内存。
- 例如,Image和BorderImage需要一个图像源,类型为url。如果图像源的属性定义为string,则需要转换,实际上它应该是url属性。
- 错误方法:
property string messageAvatar: ""
- 正确方法:
property url messageAvatar: ""
4.5 小心字符串操作
- 操作符的多次使用通常意味着多次内存分配。
- 使用StringBuilder获得更高效的字符串。QStringBuilder使用表达式模板并重新实现运算符,这样当您使用的多个子字符串连接将被推迟,直到最终结果将被分配给QString。此时,已知最终结果所需的存储量。然后调用内存分配器一次以获得所需的空间,并将子串逐个复制到其中。
- 定义QT_USE_FAST_CONCATENATION,QT_USE_FAST_OPERATOR_PLUS宏来优化字符串内存操作。
============= End