淘特 Flutter 流畅度优化实践 · 二期

2,568 阅读8分钟

淘特 Flutter 流畅度优化实践 · 二期

作者:谢伟(韦圣)

在上一篇《淘特 Flutter 流畅度优化实践》中说到,虽然一期效果较为明显,但距离极致的用户体验仍有不小的差距。去年,淘特端架构联合业务团队共同发起“基础链路极致体验优化”的项目,目标在时长与流畅度方面获得极致体验,本文将为大家详细解析淘特 Flutter 流畅度优化实践二期部分。

优化效果

首先,我们简单回顾上一期优化后的效果, 在一期中,主要的优化措施集中在“业务最佳实践”,不需要改Engine、不需要造轮子,依然取得不错的优化效果。证明Flutter在面对复杂的业务场景时, 只要掌握好足够的实践经验,依然能保持较好的性能表现。

但随着淘特的优化进入深水区,源自对极致用户体验的追求及Native性能数据的赛跑,让我们开始快速从集团、业界吸收优秀前辈的经验并自我突破。最终,在完成了“Hummer引擎升级”、“自研ExternalImage图片库”、“自研FlowView高性能流式容器”等多项重点技术突破后,取得的二期成果如下:

淘特 Flutter 流畅度优化实践 · 二期

注:流畅度随着业务迭代、测试口径的变化存在一定波动。以上数据来自:淘特4.14.0 双端慢滑测试口径(滑动速率参考下方录屏)

从体感录屏来看有两大提升:

  1. 流畅度再度提升,双端慢滑基本无卡顿
  2. 消灭iOS大卡顿问题

Android录屏对比(左边优化前、右边优化后)

查看视频请点击:淘特 Flutter 流畅度优化实践 · 二期

iOS录屏对比(左边优化前、右边优化后)

查看视频请点击:淘特 Flutter 流畅度优化实践 · 二期

优化过程中关键技术

引擎升级 Hummer

相信不少同学都听过Hummer引擎(UC Flutter定制引擎),得益于UC丰富的渲染性能优化经验及AliFlutter社区生态,淘特在充分调研后决定接入Hummer,但同时接入的过程中,淘特又与Hummer遇见了很多复杂场景需要解决的流畅度、加载耗时、内存等各方面的问题。

首先升级Hummer引擎带来的流畅度优化主要在以下4个方面,具体原理参考Hummer引擎介绍,本文侧重讲淘特实际解决的问题与解法,及升级的对比效果。

淘特 Flutter 流畅度优化实践 · 二期

下面是引擎升级的最终效果:

淘特Android4.4.0基线

淘特 Flutter 流畅度优化实践 · 二期

淘特 Flutter 流畅度优化实践 · 二期

淘特iOS4.6.0基线

淘特 Flutter 流畅度优化实践 · 二期

淘特 Flutter 流畅度优化实践 · 二期

双端在帧率、卡顿率都有了明显的提升,尤其是iOS端解决了Feeds流在iPhone6下容易大卡顿的问题。而升级引擎对淘特影响最大的就是外接图片库的升级。

Flutter图片库对比

以流畅度为例, 图片库的选择是至关重要的。由于Hummer引擎中迁移原有CDNImage(旧引擎耦合Engine的外接图片库方案)的成本过大,这迫使淘特寻找新的“非侵入引擎的图片库方案”。

最开始我们尝试用NetworkImage拿到了第一手的体验数据。流畅度提升非常明显,但NetworkImage的不足只能满足短期快速灰度,此时FImage(外接纹理的图片库方案)出现在我们面前,无疑是一个非常高效的解决方案,但实测后发现低端机流畅度较之前略有下滑,这让我们开始探寻更适合淘特的图片库方案。

最终自研ExternalImage, 在流畅度和加载耗时对比中均取得超越CDNImage的效果。

以下是集团Flutter主要的图片库方案对比:

淘特 Flutter 流畅度优化实践 · 二期

以下为Hummer下ExternalImage与外接纹理方案帧耗时性能图,我们发现外接纹理方案的Raster线程帧耗时会显著高于原生Image组件方案,原因初步分析是过多的Texture使低端机Raster耗时负载过大, 而由于frame_time近似等于max(ui,raster), 所以当raster帧耗时超越16ms也会造成实际体感FPS的降低。于是就有了下一节的ExternalImage图片库。

淘特 Flutter 流畅度优化实践 · 二期

淘特ExternalImage图片库

淘特 Flutter 流畅度优化实践 · 二期

上面是一张ExternalImage的总体架构图, 其基于FFI方式加载来自Native的像素数据,从官方Image组件出发,经Provider发起Channel调用,拿到图片返回结果后触发decode、setState等流程, 绿色代表请求链路,黄色代表回传链路。

核心技术点如下:

淘特 Flutter 流畅度优化实践 · 二期

淘特Flow-View轻量级流式容器

列表滚动一直是Flutter流畅度优化的重点场景,在未获得Hummer引擎优化前, 流畅度优化一度遇到瓶颈。在Flutter官方流式容器设计理念中,在列表滚动时, Element被视为非常轻量级的组件,没有支持复用。在列表增减元素时,Widget被认为非常轻量, 未做局部刷新。这在淘特实际复杂的Feeds业务场景下,受到了严峻挑战。我们结合业务特性,自研了一套轻量级流式容器方案Flow-View。 主要支持2个特性:

  1. 局部刷新, 将使分页加载更多无需重复build已有的itemWidget;
  2. Element/RenderObject复用, 将使滚动插入新元素时效率更高。

FlowView局部刷新

淘特 Flutter 流畅度优化实践 · 二期

我们首先看上图右侧示意图,在局部刷新场景下,左边未优化前添加新元素将setState触发整个列表Rebuild。右边优化后将只对新增的2个元素执行插入操作,已有的元素无需Rebuild。

左侧为源码细节,通过在SliverMultiBoxAdaptorElement.update方法中,通过滚动到底部,且newDelegate.childCount>oldDelegate判断为加载更多场景执行局部刷新(即插入新的元素)。

FlowView Element、RenderObject复用

淘特 Flutter 流畅度优化实践 · 二期

同样先看上图右侧示意图,在滚动场景下,当新的元素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滑动算法。

淘特 Flutter 流畅度优化实践 · 二期

我们决定在Android上根据是否滚动到尽头时区分Simulation算法。在未滚动到底部前,我们仍然用ClampingScrollSimulation保持近似Android原生的手感,滚动到尽头后为支持下拉刷新,切换至ScrollSpringSimulation。基于此动态切换的算法封装了一个通用的BouncingableClampingScrollSimulation供业务使用。

淘特 Flutter 流畅度优化实践 · 二期

总结与展望

淘特 Flutter 流畅度优化实践 · 二期

综上,在一期、二期的优化中,淘特Flutter流畅度优化在线下测试中取得了不错的效果,部分页面超过了Native。低端机也稳定在较高的帧率。但线上用户的真实场景远比线下复杂的多,所以淘特将在今年对以下三个方面做加大投入。

  1. 从线下走向线上,参与AliFlutter-APM共建、建设淘特全链路性能分析平台。
  2. 在机型方面,淘特在从之前重点关注的低端机体验优化走向全机型的体验优化,这块计划在如滑动插值器卡顿调优、 适配iphone高刷屏、升级FlutterImageView/SurfaceTexture发力。
  3. 最后, 之前的优化工作很大一部分是人工专项优化,后续将做到更多流程自动化,如在低性能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 流畅度优化实践