Flutter 动态化热更新的思考与实践(八)---- 动态列表优化v2

2,191 阅读4分钟

在之前的一篇文章中Flutter 动态化热更新的思考与实践(六)---- 动态列表滚动优化 介绍了在实现列表动态化时遇到的性能问题,当时也做了一次优化方案,其实最后的结果也不是很理想,最近抽出时间又具体做了分析并优化了新的版本。

1. 问题症结

对于列表场景而言,影响性能的原因在于每次新建一个列表项的时候Runtime都会执行很多逻辑,造成了很大的资源开销,影响了渲染帧率。而后我又复盘了一下原模板代码,发现原本的模板代码就有一些问题需要优化:

  1. 第一个问题是冗余代码比较多,这个模板代码是我们内部开发的一个工具自动生成的,这个工具还没开发成熟,导致生成的代码有很多冗余的Widget,这些冗余Widget结点会给Runtime解析带来额外的压力。
  2. 第二问题是每次新建列表项时,列表项内容的获取都通过bloc动态获取,因为从接口返回的业务数据不能直接显示,需要做些额外处理,而这些操作在动态化时,导致Runtime每次都要动态执行这部分的处理逻辑。

2. 优化方案

分析了以上的问题症结所在之后,相应的也有了优化思路。

针对第一个问题很好解决,把原模板代码中无用的Widget都删掉,一些无用的Widget属性也都删掉,尽可能的减轻Runtime的解析压力。

着重说一下第二个问题的优化思路,就是去掉每次新建列表项时需要额外执行的逻辑部分,提供一个UI数据model ,将UI需要显示的数据提前准备好,渲染UI的时候直接把UI数据赋值上就好了,而不用在渲染时临时处理UI的数据。想必大家在项目开发中也经常用到UI数据Model业务数据Model的概念,思路是一样的。

针对构造UI数据Model的思路,我们想进一步抽象出一种更通用化的模型,叫Data Sockets,该模型分为两个部分:

  • Data Plug 数据插头:一个UI组件拥有一个Data Plug,其中定义了该UI组件可以接收的数据字段
  • Data Socket 数据插线板:类比于数据池或数据流,UI组件使用自身的Data Plug 从 Data Socket 中接收数据,就好比一个插头插在插线板上。

基于以上两个概念,我们重新设计了动态列表中数据渲染方式:

dataSockets列表Sample

在新版的设计中,我们定义了一些基础UI组件的DataPlug,同时我们在搭建列表项UI的时候定义子UI组件的identifieridentifier相当于插线板上的插孔。我们从接口获取到业务数据Model后,开始组合构建DataSocketsDataSockets的结构如图中所示:

{
    "identifier1": {},
    "identifier2": {},
    ......
}

构建好后通知UI渲染,UI列表在滚动创建新列表项的时候,就根据UI组件的identifierDataSockets获取与自身DataPlug相匹配的数据,省去了其他额外的逻辑判断,直接进行数据赋值,进而优化UI的渲染效率。

看看优化后的结果如何^ ^:

dynamic_list_v2

整个列表滚动的流畅度已经在可接受的范围内了,这丝滑般的滑动,巴适~

再来对比下上一次优化前的滚动效果:

ppsyd-3mp47

提升很明显。

最后对比一下原生模板代码的运行效果:

origin_list_v2

已经比较接近原生的fps了。

3. 后记

其实我们抽象的DataSockets也不是什么新颖的概念,眼熟的朋友应该能发现和MVVM模式中的DataBind模式几乎一样的,参见Android中的DataBindingC# WinForm开发中的DataBind,也不算重复造轮子了,思想借鉴过来,根据DartFlutter相关的自身特点,量身定做符合自己业务需求的一种模式。这次的优化只是第一次实践了这个模式,抽象的还不够成熟,我们当初之所以抽象这种模式还有一个目的,就是我们内部正在开发的App设计工具,未来想要实现业务逻辑功能的可视化编辑,也是目前比较流行的低代码或无代码理念,方便非研发人员也可以开发app,相关的产品可参见Microsoft Power Platform,对此感兴趣的童鞋欢迎在评论区留言讨论~