共计 3705 个字符,预计需要花费 10 分钟才能阅读完成。
作者:谢伟 (韦圣)
在上一篇《淘特 Flutter 晦涩度优化实际》中说到,尽管一期成果较为显著,但间隔极致的用户体验仍有不小的差距。去年,淘特端架构联结业务团队独特发动“根底链路极致体验优化”的我的项目,指标在时长与晦涩度方面取得极致体验,本文将为大家具体解析淘特 Flutter 晦涩度优化实际二期局部。
优化成果
首先,咱们简略回顾上一期优化后的成果,在一期中,次要的优化措施集中在“业务最佳实际”,不须要改 Engine、不须要造轮子,仍然获得不错的优化成果。证实 Flutter 在面对简单的业务场景时,只有把握好足够的实践经验,仍然能放弃较好的性能体现。
但随着淘特的优化进入深水区,源自对极致用户体验的谋求及 Native 性能数据的赛跑,让咱们开始疾速从团体、业界排汇优良前辈的教训并自我冲破。最终,在实现了“Hummer 引擎降级”、“自研 ExternalImage 图片库”、“自研 FlowView 高性能流式容器”等多项重点技术冲破后,获得的二期成绩如下:
注:晦涩度随着业务迭代、测试口径的变动存在肯定稳定。以上数据来自:淘特 4.14.0 双端慢滑测试口径(滑动速率参考下方录屏)
从体感录屏来看有两大晋升:
- 晦涩度再度晋升,双端慢滑根本无卡顿
- 毁灭 iOS 大卡顿问题
Android 录屏比照(右边优化前、左边优化后)
查看视频请点击:淘特 Flutter 晦涩度优化实际 · 二期
iOS 录屏比照(右边优化前、左边优化后)
查看视频请点击:淘特 Flutter 晦涩度优化实际 · 二期
优化过程中关键技术
引擎降级 Hummer
置信不少同学都听过 Hummer 引擎(UC Flutter 定制引擎),得益于 UC 丰盛的渲染性能优化教训及 AliFlutter 社区生态,淘特在充沛调研后决定接入 Hummer,但同时接入的过程中,淘特又与 Hummer 遇见了很多简单场景须要解决的晦涩度、加载耗时、内存等各方面的问题。
首先降级 Hummer 引擎带来的晦涩度优化次要在以下 4 个方面,具体原理参考 Hummer 引擎介绍,本文偏重讲淘特理论解决的问题与解法,及降级的比照成果。
上面是引擎降级的最终成果:
淘特 Android4.4.0 基线
淘特 iOS4.6.0 基线
双端在帧率、卡顿率都有了显著的晋升,尤其是 iOS 端解决了 Feeds 流在 iPhone6 下容易大卡顿的问题。而降级引擎对淘特影响最大的就是外接图片库的降级。
Flutter 图片库比照
以晦涩度为例,图片库的抉择是至关重要的。因为 Hummer 引擎中迁徙原有 CDNImage(旧引擎耦合 Engine 的外接图片库计划)的老本过大,这迫使淘特寻找新的“非侵入引擎的图片库计划”。
最开始咱们尝试用 NetworkImage 拿到了第一手的体验数据。晦涩度晋升非常明显,但 NetworkImage 的有余只能满足短期疾速灰度,此时 FImage(外接纹理的图片库计划)呈现在咱们背后,无疑是一个十分高效的解决方案,但实测后发现低端机晦涩度较之前略有下滑,这让咱们开始探寻更适宜淘特的图片库计划。
最终自研 ExternalImage,在晦涩度和加载耗时比照中均获得超过 CDNImage 的成果。
以下是团体 Flutter 次要的图片库计划比照:
以下为 Hummer 下 ExternalImage 与外接纹理计划帧耗时性能图,咱们发现外接纹理计划的 Raster 线程帧耗时会显著高于原生 Image 组件计划,起因初步剖析是过多的 Texture 使低端机 Raster 耗时负载过大,而因为 frame_time 近似等于 max(ui,raster),所以当 raster 帧耗时超过 16ms 也会造成理论体感 FPS 的升高。于是就有了下一节的 ExternalImage 图片库。
淘特 ExternalImage 图片库
下面是一张 ExternalImage 的总体架构图,其基于 FFI 形式加载来自 Native 的像素数据,从官网 Image 组件登程,经 Provider 发动 Channel 调用,拿到图片返回后果后触发 decode、setState 等流程,绿色代表申请链路,黄色代表回传链路。
核心技术点如下:
淘特 Flow-View 轻量级流式容器
列表滚动始终是 Flutter 晦涩度优化的重点场景,在未取得 Hummer 引擎优化前,晦涩度优化一度遇到瓶颈。在 Flutter 官网流式容器设计理念中,在列表滚动时,Element 被视为十分轻量级的组件,没有反对复用。在列表增减元素时,Widget 被认为十分轻量,未做部分刷新。这在淘特理论简单的 Feeds 业务场景下,受到了严厉挑战。咱们联合业务个性,自研了一套轻量级流式容器计划 Flow-View。次要反对 2 个个性:
- 部分刷新,将使分页加载更多无需反复 build 已有的 itemWidget;
- Element/RenderObject 复用,将使滚动插入新元素时效率更高。
FlowView 部分刷新
咱们首先看上图右侧示意图,在部分刷新场景下,右边未优化前增加新元素将 setState 触发整个列表 Rebuild。左边优化后将只对新增的 2 个元素执行插入操作,已有的元素无需 Rebuild。
左侧为源码细节,通过在 SliverMultiBoxAdaptorElement.update 办法中,通过滚动到底部,且 newDelegate.childCount>oldDelegate 判断为加载更多场景执行部分刷新(即插入新的元素)。
FlowView Element、RenderObject 复用
同样先看上图右侧示意图,在滚动场景下,当新的元素 12、13 行将入屏时,右边未优化前将创立全新的 Element、RenderObject。左边优化后新元素 12、13 将复用顶部移出的 0、1 元素的 Element、RenderObject。做到循环利用,效率更高。
左侧为源码细节,当获取新插入的 item 时,通过在 SliverMultiBoxAdaptorElement.createChild 办法中,未优化前_childElements[index]=null 将触发 Element、RenderObjec 新建,优化后将先依据新元素的类型找是否有可复用的元素,再触发 updateChild,若缓存不为空,则会执行 didUpdateWidget 逻辑。
当移除元素时,removeChild 将不再 deactive Element(即不触发 updateChild)。同时通过批改 framework 将 RenderObject 从 ContainerRenderObjectMixin 双向链表中移除(renderObject._removeFromChildList)。再将 Element 增加进 cacheMap 即可。
Android 滑动手感
在淘特,不仅关注晦涩度数据的晋升,更关注用户理论的体感。在某一次版本升级后,Android 的手感不如以前顺滑,滑动初期阻尼感升高显著。如下视频比照。
1. 优化前 4.2.0
查看视频请点击:淘特 Flutter 晦涩度优化实际 · 二期
2. 优化后 4.3.0
查看视频请点击:淘特 Flutter 晦涩度优化实际 · 二期
剖析起因是 基于 BouncingScrollSimulation 实现的下拉刷新组件扭转了 Android 平台原有的 Simulation。通过剖析 Android 和 iOS 平台的 Simulation 滑动算法。
咱们决定在 Android 上依据是否滚动到止境时辨别 Simulation 算法。在未滚动到底部前,咱们依然用 ClampingScrollSimulation 放弃近似 Android 原生的手感,滚动到止境后为反对下拉刷新,切换至 ScrollSpringSimulation。基于此动静切换的算法封装了一个通用的 BouncingableClampingScrollSimulation 供业务应用。
总结与瞻望
综上,在一期、二期的优化中,淘特 Flutter 晦涩度优化在线下测试中获得了不错的成果,局部页面超过了 Native。低端机也稳固在较高的帧率。但线上用户的实在场景远比线下简单的多,所以淘特将在往年对以下三个方面做加大投入。
- 从线下走向线上,参加 AliFlutter-APM 共建、建设淘特全链路性能剖析平台。
- 在机型方面,淘特在从之前重点关注的低端机体验优化走向全机型的体验优化,这块打算在如滑动插值器卡顿调优、适配 iphone 高刷屏、降级 FlutterImageView/SurfaceTexture 发力。
- 最初,之前的优化工作很大一部分是人工专项优化,后续将做到更多流程自动化,如在低性能 Widget 告警工具、集成性能卡口工具等方面发力,保障业务高质量低成本放弃优化成绩。
【参考文献】
[1]:Google Flutter 团队 Xiao Yu:Flutter Performance Profiling and Theory
[2]:闲鱼 云从: 他把闲鱼 APP 长列表晦涩度翻了倍
[3]:Google Android RecyclerView.ViewHolder:RecyclerView.Adapter#onCreateViewHolder)
[4]:Jank 卡顿及 stutter 卡顿率阐明
[5]:淘特 Flutter 晦涩度优化实际