作者:谢伟(韦圣)
在上一篇《淘特 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 晦涩度优化实际