前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter如何设计一个高性能,多功能的ListView组件

Flutter如何设计一个高性能,多功能的ListView组件

作者头像
用户6256742
发布2024-06-01 10:18:01
670
发布2024-06-01 10:18:01
举报
文章被收录于专栏:网络日志网络日志

Flutter如何设计一个高性能,多功能的ListView组件

学习最忌盲目,无计划,零碎的知识点无法串成系统。学到哪,忘到哪,面试想不起来。这里我整理了Flutter面试中最常问以及Flutter framework中最核心的几块知识,大概化二十篇左右文章分析,欢迎关注,共同进步。![Flutter framework]

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

欢迎搜索公众号:进击的Flutter或者runflutter 里面整理收集了最详细的Flutter进阶与优化指南。关注我,获取我的最新文章~

导语:

实战篇:

1、Flutter如何设计一个高性能,多功能的ListView组件

2、如何解决特定场景下ListView中存在的性能问题

3、开源!!!!

PS:组件目前已经完成了功能上的开发,目前正在持续做性能上优化,即将开源,关注点赞不要错过最新信息!!


一、多功能的ListView组件需要提供哪些能力?

既然我们号称高性能,多功能的ListView,那这个组件该包含哪些能力?首先我会认为,无论做组件还是架构,我们的设计应该尽量保证每个模块的功能单一并且完善。虽然我们号称多功能,但是组件本质任然只是一个ListView,所以提供的能力应该是围绕可以滚动的列表出发。结合闲鱼的文章与个人的日常使用,我认为ListView还欠缺下面几种能力。

1、滚动到指定index

我们在Flutter中可以通过使用ScrollController控制ListView滚动到指定的位置,但这里的位置是基于offset(偏移像素)而非index,实际开发中我们常常会用到跳转指定index的能力。例如,我们想要实现tab与列表的联动,点击tab跳转到指定的列表位置。

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

这个时候,如果我们的跳转能基于index,那么这个功能就非常好实现了。

2、自动曝光能力

业务场景中,我们经常需要对列表中的item做曝光处理。当前,我们往往会在item的build函数或者initState中进行,但由于ListView的预加载和垃圾回收机制,一些未出现在屏幕上的会被提前曝光。对于曝光过的item可能因为被回收后进行二次构建,会再次走曝光逻辑。这就要求我们在业务代码中增加额外的逻辑,处理起来非常不合理。

曝光能力其实是获取屏幕上可见的item的衍生,所以同样的,组件也该包含这样的能力。

3、垃圾回收的回调通知

这点我们同事在实际的业务场景中遇到过,对于列表加载多图,即使划出屏幕的图片组件element被回收,但图片缓存任然累积在内存中,当时引起了大量的OOM,最后通过外界纹理的方案解决了这个问题。虽然我也认为,这样的问题应该在控件内部解决,但是如果有垃圾回收的回调通知,那么假如以后列表的item换成了视频,或者其他类型的控件,我们处理起来会更加灵活一点。

PS:(上面的功能都已经实现了~~)


二、高性能的ListView组件要解决哪些场景?

上面是对于功能的设计,那么从性能角度闲鱼在文章中也提到了我们遇到的一些问题:

1、LoadMore场景下的增量更新

我们在使用ListView的时候,往往会配合刷新组件做加载更多的功能。很多时候,我们都会在获取到更多数据,后调用setState更新列表UI,但调用setState之后,SliverMultiBoxAdaptorElement会对当前屏幕上以及缓存区中所有的element更新,在这个时间节点,非常容易引起列表的卡顿。

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

2、Element的缓存

3、分帧上屏


三、每种功能的实现方案

在明确了功能需求之后,我并没有着急动手开发,而是先思考这些功能的在实现上的基本方案以及他们之间的联系(本期以功能分析为主,下期会进行性能上的分析)。

  • 滚动到指定的index 这个功能目前已经有很多的开源方案,我了解下来发现主要有两种思路:

1、重新构建视窗,指定我们需要跳转index的Widget到当前视窗的顶部。例如 indexed_list_view

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

这种方法思路比较简单,不过emmmm咋说呢,这效果也太粗暴了点吧。

2、缓存每个item的高度,指定滚动index的时候去计算需要滚动的offset 。例如list_view_item_builder

这个思路挺不错,不过里面滚动逻辑写有些复杂,而且我在运行example的时候还出现了bug,对于超出屏幕的index,有时并不能直接跳转到我们需要item上。

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

(指定了section是6和5,多次跳转才成功)

但个人感觉这个方案稍微温柔一点,所以最终参考了这个思路,并且完全重新实现了这个能力。

  • 自动曝光能力(获取屏幕可见Widget)

自动曝光本质上是回调给使用者 我们当前屏幕上有哪些可见的Widget。基于我们获取到了每一个item的Size信息之后,这个问题就迎刃而解了。

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

我们把itme进行排列,将ListView想象成一个窗口。滑动的时候基于offset改变窗口的位置以显示不同的item。根据偏移量和窗口的高度我们可以得到 可视范围的起点和终点,再基于item的高度缓存信息,便可计算出当前屏幕上的item。为了减少这个方法频繁的计算,我们可以增加一个采样范围,当列表的滑动超过某个阈值的时候我们才会进行计算。再通过一个map记录已经被曝光过的item,确保每个item只会被曝光一次。

  • 垃圾回收的回调通知 这点相对比较简单,因为虽然垃圾回收是从RenderSliverList中performLayout()调用的,但是最终任然会走到Element的void removeChild(RenderBox child)中。所以可以通过Element将每次被销毁调的child通知去释放资源。但是这个会和我们在性能优化中提到的Element复用有关,设计的时候也要考虑这个问题。

四、组件整体结构设计

首先我们看看当前ListView中主要的几个类之间关系

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

平时我们都是直接使用ListView,但要先实现我们上面提到的功能,我们需要对ListView进行深度的定制。例如,上面提到,要给每一个item嵌套一个代理Widget发送通知测量的尺寸信息,那么我们可以选择重写SliverChildBuilderDelegate的build方法,在其中对应插入我们需要嵌套的Widget。有了消息的发送者必然需要在这个结构中插入接受者,这里我参考了PageView的实现,选择嵌套到ListView中收集尺寸信息,将这个信息传递给自定义的ScrollController,由他实现指定index的滚动。

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

上面是最终的类关系图,为了区别系统的组件,我为所有涉及修改的类都加上了BK作为关键字(我对我司爱的深沉)。蓝色部分为主要修改的地方,在这个结构中最大的改变是,引入的一个新的成员BKNotifier,他的主要是为其他类提供itemCount的信息,以及用于我们对列表的item进行增,删操作的时候提高效率。

抛去他们的引用关系,从功能的角度上看,他们之间存在这样的关系(不同功能采用不同颜色的虚线)

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

如果你还想了解更多信息,欢迎评论区交流。


总结

最后放上一张目前已经实现的功能图~,所有功能正在验证中,性能还在开发~

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

增量更新下的性能数据,debug下时间从320ms->100ms,约60%+(时间不重要,release下不会这么耗时,要关注提升的效率)

Flutter如何设计一个高性能,多功能的ListView组件
Flutter如何设计一个高性能,多功能的ListView组件

目前组件开发已经已经进入尾声,争取两周之内和大家见面。


最后 感谢各位彭于晏 吴彦祖的点赞和评论!!!

本期主要从功能设计的角度分享我的思路。以前在做功能模块设计的时候,我往往会先陷入局部的细节,这样越做到后面会发现问题越多,大大的增加了整体上的实现难度。这次翻了翻大学的软件工程资料,尝试自顶向下的解决问题,遵循软件开发流程,考虑各个模块之间的联系,很多问题就暴露在了开始,整个开发过程流畅了许多。

下期将会介绍性能方面的优化,涉及一些原理上的内容,推荐阅读我之前对于原理部分的文章,希望能加深你对Flutter framework的理解。

PS:感谢各位彭于晏 吴彦祖的点赞和评论!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flutter如何设计一个高性能,多功能的ListView组件
    • 导语:
      • 一、多功能的ListView组件需要提供哪些能力?
        • 1、滚动到指定index
        • 2、自动曝光能力
        • 3、垃圾回收的回调通知
      • 二、高性能的ListView组件要解决哪些场景?
        • 1、LoadMore场景下的增量更新
        • 2、Element的缓存
        • 3、分帧上屏
      • 三、每种功能的实现方案
        • 四、组件整体结构设计
          • 总结
            • 最后 感谢各位彭于晏 吴彦祖的点赞和评论!!!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档