最后
以前一直是自己在网上东平西凑的找,找到的东西也是零零散散,很多时候都是看着看着就没了,时间浪费了,问题却还没得到解决,很让人抓狂。
后面我就自己整理了一套资料,还别说,真香!
资料有条理,有系统,还很全面,我不方便直接放出来,大家可以先看看有没有用得到的地方吧。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-
长列表loadmore优化
-
滚动加载小图
Flutter卡顿定位工具建设
Flutter采用数据驱动的方式构建UI,官方的Flutter性能最佳实践[1]指出
-
避免在 build() 方法中进行重复且耗时的工作,因为当父 Widget 重建时,子 Wdiget 的 build() 方法会被频繁地调用
-
避免在一个超长的 build() 方法中返回一个过于庞大的 Widget。把他们分拆成不同的 Widget,并进行封装
但是随着业务快速迭代,开发者往往会疏忽大意,写出低性能的代码。理想情况是,通过性能分析工具自动定位到问题代码,及时修改。Flutter官方提供了Devtools性能分析工具,它的timeline界面可以逐帧分析应用的UI性能,cpu profiler界面来抓取卡顿过程中的耗时操作。在研发阶段,我们可以使用这个工具来分析本地可以复现的卡顿问题。但是,Devtools的局限性在于:
-
只能用于在出现卡顿问题后去分析,无法做到监控卡顿问题。
-
只能用于线下排查,对于线上反馈卡顿问题,因为没有足够的场景,也无能为力。
为此我们需要建设一个Flutter卡顿工具,用于在线上线下监控定位造成卡顿的耗时函数。实现思路:我们知道release模式下的Dart代码是基于AOT编译的,业务代码和sdk都会编译成平台相关的机器码,所以我们是可以通过信号机制抓取native的堆栈,再通过符号化还原的方式来获取flutter堆栈。
我们通过卡顿工具定位排查出两类造成流畅度低的问题:耗时函数和过度渲染问题。
定位耗时函数
对于耗时函数导致的卡顿问题,可以通过查看堆栈定位到耗时函数。
从调用栈可以看出channel的调用时数据序列化 和 反序列化比较耗时。通过查看FxImage的代码,了解到resumeImage对应的native实现已经为空,flutter侧其实可以省去这一次channel调用。
定位过度渲染
在自动化阶段上报的数据有大量是如下所示渲染阶段的堆栈,无法定位到业务代码。
这是由于Flutter的刷新机制是中心化、异步的渲染机制, 业务层需要刷新界面元素,framework层会先把需要更新的元素标脏,然后等下一次引擎侧的渲染回调,在这次渲染回调中,对之前收集到的脏元素统一做更新。这样的机制导致在抓取渲染阶段的堆栈有大量的Element.rebuild、update方法,都是flutter framework的函数调用栈,而缺失了业务侧代码,只看这些堆栈并不能帮我们定位到卡顿问题。
我们的思路是:通过增加渲染过程中的脏element信息, 根据标脏的element的复杂程度(我们根据元素的深度、长度、子节点数来计算复杂度) 可以帮助我们检测出是否有代码不规范导致刷新范围过大的问题。
通过这个方案,我们定位到详情页快速提问组件过度渲染问题:
查看脏element信息可以看到,打脏的元素复杂度较高。优化方案:提高build效率,setState刷新数据下沉到叶子节点,将每个标签抽离成LabelStatefulWidget,只做局部刷新。
长列表loadmore优化
通过卡顿工具,我们发现搜索结果页列表滑动加载更多过程中会对整个列表容器打脏重建,造成比较严重的流畅度问题。经过分析发现,因为加载更多数据返回后,追加列表数据后会调用setState,打脏容器widget。并且由于我们有做预加载,在滑到底部前几屏时就去触发加载更多的逻辑,导致打脏重建widget的频率更高。一期我们从业务层入手,预加载更多的数据回来并不去调用setState,而先是保存到内存,等滑到底部再去调用setState,重建列表容器。后面我们从列表容器底层去优化,loadmore时不会打脏重建整个列表容器,容器加载和滑动过程有较大的优化,提升体验比较明显。
优化前如下图,(红色区域表示打脏的widget)可以看到刷新了整个容器。
[图片上传失败…(image-88d1df-1607610735943)]
优化后,仅对新增的卡片做刷新
[图片上传失败…(image-d1416d-1607610735943)]
滚动加载小图
feeds流卡片包含大量的图片,在快速滑动过程中,加载大量图片对于内存和IO都是比较大的考验,影响流畅度,在低端机上尤其明显。优化思路:在快速滚动过程中,只加载尺寸较小的模糊图,等到滚动停止后再渐进式的展示原图,并且在超出屏幕区域不加载原图,优化上屏体验。效果如图:(为了演示效果,这里的用的是缩小5倍的小图)
[图片上传失败…(image-853675-1607610735943)]
小结
在经过优化之后,闲鱼详情页和搜索页流畅度 FPS 提升了 3 个点,低端机大卡顿次数降低一半,中高端机型上流畅度提升到 57 或以上,大卡顿次数接近 0。
详情页线上高可用 fps 数据如下:
线上低端机 fps 曲线,横轴表示帧率区间,绿色为优化版本。曲线分布越靠右,流畅度越好。
线上高端机 fps 曲线。绿色为优化版本
搜索页线上高可用 fps 数据如下:
线上低端机 fps 曲线。绿色为优化版本
线上高端机 fps 曲线。绿色为优化版本
在优化流畅度问题之后,再来看下对于页面加载需要做哪些优化。在优化之前,从搜索关键词到搜索结果展示过程中有较长loading。对于页面的加载速度优化,我们更多的从业务流程开始去找突破口,搜索结果页的打开过程如下:
搜索结果页由Flutter实现,但它是从Native页面点击打开,在混合栈的背景下导致路由拦截到打开容器这一步有一定耗时。我们可以通过 URL 携带预取信息,在 Native 进行跳转导航时同时进行异步并行的数据预取,可以减少页面打开的耗时。同时因为搜索页面的请求RT相对比较高,一般页面进来了,还仍然在等待网络请求回来,所以如果在网络请求回来的时候再去做模板的预加载,大概率会命中。
优化之后的流程如下:
通过一定的并行手段,采用数据预取、模板预加载的方案,我们在Android低端机上将搜索结果页加载时长优化300ms。同时在数据请求时展示骨架屏动画(lottie实现)代替小黄鱼loading,带给用户更好的使用体验。
优化前:
[图片上传失败…(image-45c8ba-1607610735943)]
优化后:
[图片上传失败…(image-e347e6-1607610735943)]
对于详情页的加载优化,我们主要通过下面3个手段做优化:
最后
下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术
高级UI,自定义View
UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。
不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
去项目实战,读源码,研究原理吧!
[外链图片转存中…(img-dirFI1RQ-1715892006229)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!