已开源!Flutter 流畅度优化组件 Keframe | 开发者说·DTalk

2022 年 1 月 7 日 谷歌开发者

本文原作者: Nayuta原文发布于: 进击的 Flutter



列表流畅度优化


这是一个通用的流畅度优化方案,通过分帧渲染优化由构建导致的卡顿,例如页面切换或者复杂列表快速滚动的场景。

代码中 example 运行在 VIVO X23 (骁龙 660),在相同的滚动操作下优化前后 200 帧采集数据指标对比 (录屏在文章最后):

优化前
优化后

△ 监控工具来自: fps_monitor

指标详细信息: https://juejin.cn/post/6947911434424549384

  • 流畅: 一帧耗时低于 18ms

  • 良好: 一帧耗时在 18ms-33ms 之间

  • 轻微卡顿: 一帧耗时在 33ms-67ms 之间

  • 卡顿: 一帧耗时大于 66.7ms


采用分帧优化后,卡顿次数从平均 33.3 帧出现了一帧,降低到 200 帧中仅出现了一帧,峰值也从 188ms 降低到 90ms。卡顿现象大幅减轻,流畅帧占比显著提升,整体表现更流畅。下方是详细数据。


优化前
优化后
平均多少帧出现一帧卡顿 33.3
200
平均多少帧出现一帧轻微卡顿 8.6
66.7
最大耗时 188.0ms
90.0ms
平均耗时 27.0ms
19.4ms
流畅帧占比 40%
64.5%



页面切换流畅度提升


在打开一个页面或者 Tab 切换时,系统会渲染整个页面并结合动画完成页面切换。对于复杂页面,同样会出现卡顿掉帧。借助分帧组件,将页面的构建逐帧拆解,通过 DevTools 中的性能工具查看。切换过程的峰值由 112.5ms 降低到 30.2 ms,整体切换过程更加流畅。



如何使用


项目依赖:

pubspec.yaml 中添加 keframe 依赖:
dependencies:  keframe: version

组件仅区分非空安全与空安全版本:

非空安全使用: 1.0.2

空安全版本使用: 2.0.2

github 地址: https://github.com/LianjiaTech/keframe

pub 查看: https://pub.dev/packages/keframe


快速上手:

如下图所示

假如现在页面由 A、B、C、D 四部分组成,每部分耗时 10ms,在页面时构建为 40ms。使用分帧组件 FrameSeparateWidget 嵌套每一个部分。页面构建时会在第一帧渲染简单的占位,在后续四帧内分别渲染 A、B、C、D。


对于列表,在每一个 item 中嵌套 FrameSeparateWidget,并将 ListView 嵌套在 SizeCacheWidget 内即可。



构造函数说明


FrameSeparateWidget: 分帧组件,将嵌套的 widget 单独一帧渲染

类型
参数名
是否必填
含义
Key key

int index
分帧组件 id,使用 SizeCacheWidget 的场景必传,SizeCacheWidget 中维护了 index 对应的 Size 信息
Widget child 实际需要渲染的 widget
Widget placeHolder
占位 widget,尽量设置简单的占位,不传默认是 Container()


SizeCacheWidget: 缓存子节点中,分帧组件嵌套的实际 widget 的尺寸信息

类型
参数名
是否必填
含义
Key key
Widget child 子节点中如果包含分帧组件,则缓存实际的 widget 尺寸

int


estimateCount
预估屏幕上子节点的数量,提高快速滚动时的响应速度



方案设计与分析


卡顿的本质,就是单帧的绘制时间过长。基于此自然衍生出两种思路解决:

  1. 减少一帧的绘制耗时,因为导致耗时过长的原因有很多,比如不合理的刷新,或者绘制时间过长,都有可能,需要具体问题具体分析,后面我会分享一些我的优化经验。
  2. 在不对耗时优化下,将一帧的任务拆分到多帧内,保证每一帧都不超时。这也是本组件的设计思路,分帧渲染。


如下图所示:

原理并不复杂,问题在于如何在 Flutter 中实践这一机制。


因为涉及到帧与系统的调度,自然联想到看 SchedulerBinding 中有无现成的 API。


发现了 scheduleTask 方法,这是系统提供的一个执行任务的方法,但这个方法存在两个问题:

  1. 其中的渲染任务是优先级进行堆排序,而堆排序是不稳定排序,这会导致任务的执行顺序并非 FIFO。从效果上来看,就是列表不会按照顺序渲染,而是会出现跳动渲染的情况

  2. 这个方法本身存在调度问题,我已经提交 issue 与 pr。


最终,参考这个设计结合 endOfFrame 方法的使用,完成了分帧队列。整个渲染流程变为下图所示:

对于列表构建场景来说,假设屏幕上能显示五个 item。首先在第一帧的时候,列表会渲染 5 个占位的 Widget,同时添加 5 个高优先级任务到队列中,这里的任务可以是简单的将占位 Widget 和实际 item 进行替换,也可通过渐变等动画提升体验。在后续的五帧中占位 Widget 依次被替换成实际的列表 item。



一些展示效果 (Example 说明请查看 Github)


  • Github

    https://github.com/LianjiaTech/keframe/blob/master/README-ZH.md


卡顿的页面往往都是由多个复杂 widget 同时渲染导致。通过为复杂的 widget 嵌套分帧组件 FrameSeparateWidget。渲染时,分帧组件会在第一帧同时渲染多个 palceHolder,之后连续的多帧内依次渲染复杂子项,以此提升页面流畅度。


例如 example 中的优化前示例:

ListView.builder(              itemCount: childCount,              itemBuilder: (c, i) => CellWidget(                color: i % 2 == 0 ? Colors.red : Colors.blue,                index: i,              ),            )

其中 CellWidget 高度为 60,内部嵌套了三个 TextField 的组件 (整体构建耗时在 9ms 左右)。


优化仅需为每一个 item 嵌套分帧组件,并为其设置 placeHolder (placeHolder 尽量简单,样式与实际 item 接近即可)。


在列表情况下,给 ListView 嵌套 SizeCacheWidget,同时建议将预加载范围 cacheExtent 设置大一点,例如 500 (该属性默认为 250),提升慢速滑动时候的体验。

△ 占位与实际列表项不一致时
首次渲染抖动,二次渲染正常

此外,也可以给 item 嵌套透明度/位移等动画,优化视觉上的效果。


效果如下图:



分帧的成本


当然分帧方案也非十全十美,在我看来主要有两点成本:

  1. 额外的构建开销: 整个构建过程的构建消耗由「n * widget消耗 」变成了「n * (widget + 占位) 消耗 + 系统调度 n 帧消耗」。可以看出,额外的开销主要由占位的复杂度决定。如果占位只是简单的 Container,测试后发现整体构建耗时大概提升在 15 % 左右。这种额外开销对于当下的移动设备而言,成本几乎可以不计。
  2. 视觉上的变化: 如同上面的演示,组件会将 item 分帧渲染,页面在视觉上出现占位变成实际 widget 的过程。但其实由于列表存在缓存区域 (建议将缓存区调大),在高端机或正常滑动情况下用户并无感知。而在中低端设备上快速滑动能感觉到切换的过程,但比严重顿挫要好。


优化前后对比演示


注: gif 帧率只有 20

优化前 优化后



最后: 一点点思考


列表优化篇到此告一段落,在整个开源实践过程中,有两点感触较深:

「点」与「面」的关系
我们在思考技术方案的时候可以由「点」到「面」,站在一个较高视野去想问题的本质。

而在执行的时候则需要由「面」到「点」的进行逐级拆分,抓住问题的关键节点,并且拟定进度计划,逐步破解。

很多时候,这种向上和向下的逻辑思维才是我们的核心竞争力

以不变应万变
对于未知的东西,我们往往会过度的将它想复杂。在一开始分析列表构建原理的时候,我也苦于无从下手,走了很多弯路。但其实对于 Flutter 这套「UI」框架而言,核心仍然在于三棵树的构建机制

在这套体系内,抓住不变的东西,无论是生命周期、路由等等问题都可以从里面找到答案。




长按右侧二维码

查看更多开发者精彩分享




"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。



 

 点击屏末 |  | 即刻报名参与 "开发者说·DTalk" 

 



登录查看更多
0

相关内容

多媒体系统(MS)期刊详细介绍了多媒体计算,通信,存储和应用的各个方面的创新研究思想,新兴技术,最新方法和工具。它包含理论,实验和调查文章。多媒体系统的覆盖范围包括:在计算机系统中集成数字视频和音频功能;多媒体信息编码和数据交换格式;数字多媒体的操作系统机制;数字视频和音频网络与通信;存储模型和结构;用于支持多媒体应用程序的方法、范式、工具和软件体系结构;多媒体应用程序和应用程序接口,以及多媒体终端系统架构。 官网地址:http://dblp.uni-trier.de/db/journals/mms/
​【CVPR 2021】半监督视频目标分割新算法,实现SOTA性能
专知会员服务
12+阅读 · 2021年4月26日
Google-EfficientNet v2来了!更快,更小,更强!
专知会员服务
18+阅读 · 2021年4月4日
【硬核书】Linux核心编程|Linux Kernel Programming,741页pdf
专知会员服务
76+阅读 · 2021年3月26日
分布式图神经知识表示框架
专知会员服务
61+阅读 · 2020年7月28日
一图搞定ML!2020版机器学习技术路线图,35页ppt
专知会员服务
92+阅读 · 2020年7月28日
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
【电子书】Flutter实战305页PDF免费下载
专知会员服务
20+阅读 · 2019年11月7日
Flutter 快速解析 TextField 的内部原理 | 开发者说·DTalk
如何理解 Flutter 路由源码设计?| 开发者说·DTalk
谷歌开发者
1+阅读 · 2022年1月28日
Flutter 如何与 Native (Android) 进行交互 | 开发者说·DTalk
新版本系统适配: Android 12 中的兼容性变更
谷歌开发者
0+阅读 · 2022年1月13日
Flutter 之美 | 开发者说·DTalk
谷歌开发者
1+阅读 · 2021年12月23日
跨平台开发框架 Compose Multiplatform 1.0 发布
国家自然科学基金
0+阅读 · 2014年12月31日
国家自然科学基金
0+阅读 · 2014年12月31日
国家自然科学基金
1+阅读 · 2014年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2009年12月31日
SkiQL: A Unified Schema Query Language
Arxiv
0+阅读 · 2022年4月19日
Arxiv
0+阅读 · 2022年4月18日
Deformable Style Transfer
Arxiv
14+阅读 · 2020年3月24日
Arxiv
12+阅读 · 2018年1月20日
VIP会员
相关VIP内容
​【CVPR 2021】半监督视频目标分割新算法,实现SOTA性能
专知会员服务
12+阅读 · 2021年4月26日
Google-EfficientNet v2来了!更快,更小,更强!
专知会员服务
18+阅读 · 2021年4月4日
【硬核书】Linux核心编程|Linux Kernel Programming,741页pdf
专知会员服务
76+阅读 · 2021年3月26日
分布式图神经知识表示框架
专知会员服务
61+阅读 · 2020年7月28日
一图搞定ML!2020版机器学习技术路线图,35页ppt
专知会员服务
92+阅读 · 2020年7月28日
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
【电子书】Flutter实战305页PDF免费下载
专知会员服务
20+阅读 · 2019年11月7日
相关资讯
Flutter 快速解析 TextField 的内部原理 | 开发者说·DTalk
如何理解 Flutter 路由源码设计?| 开发者说·DTalk
谷歌开发者
1+阅读 · 2022年1月28日
Flutter 如何与 Native (Android) 进行交互 | 开发者说·DTalk
新版本系统适配: Android 12 中的兼容性变更
谷歌开发者
0+阅读 · 2022年1月13日
Flutter 之美 | 开发者说·DTalk
谷歌开发者
1+阅读 · 2021年12月23日
跨平台开发框架 Compose Multiplatform 1.0 发布
相关基金
国家自然科学基金
0+阅读 · 2014年12月31日
国家自然科学基金
0+阅读 · 2014年12月31日
国家自然科学基金
1+阅读 · 2014年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2009年12月31日
Top
微信扫码咨询专知VIP会员