前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MOO音乐的Flutter实战总结之内存治理(下)

MOO音乐的Flutter实战总结之内存治理(下)

作者头像
QQ音乐技术团队
发布2021-08-23 11:10:23
1.6K0
发布2021-08-23 11:10:23
举报
文章被收录于专栏:QQ音乐技术团队的专栏

MOO 音乐是 TME 旗下的新锐音乐服务,其团队是公司内最早实践 Flutter 的先行者之一。本系列文章将提炼 MOO APP 开发中遇到的情况,就 Flutter 内存占用治理方面,分享日常开发的一些基本认知、注意要点、排查方法和优化方案。内存治理篇文章共分上、中、下三篇,本篇为下篇。

五、内存优化策略

1. 图片内存优化

各种导致内存增长的资源中,图片引起的问题是尤为明显和常见的,一张高清图动辄几百K,MOO 音乐很多列表都使用 GIF 动图,大小可以达几MB乃至十几MB,图片所占内存跟图片大小有关,影响更大的是图片缓存尺寸导致的内存增长,一旦出问题就很容易导致 OOM。

要实现图片内存优化,我们从图片加载流程入手,分析可以从哪些处理节点作为优化的切入点,图一是 NetworkImage 的加载过程。

图一

i. 图片缓存尺寸(即解码尺寸)优化

从源码可以看到,Image.network、Image.asset、Image.file、Image.memory 都有执行设置缓存尺寸的 resize,如果没有设置 cacheWidth 和 cacheHeight,默认使用的是图片自身的像素尺寸。

根据框架源码,具体图片所占内存的计算方法为:图片占用内存大小 = 解码宽度像素 * 解码高度像素 * 4,也就是图片解码数据内存占用跟解码面积成正比。

下面做个简单的实验来验证设置缓存尺寸对内存大小的影响程度。

直接加载一张 @3x 尺寸为 2058x1800 的图片。

不设置缓存尺寸引擎会以原图的尺寸作为解码尺寸,也就是 2058x1800,解码内存达到了 18.8MB,如图二所示。

图二

这里的缓存尺寸只设置了 cacheWidth,cacheHeight 会自动根据图片原比例计算得出。

设置了缓存尺寸,图片解码内存占用只有 5.2MB,如图三所示。

图三

那么,缓存尺寸该如何取值呢?

相对屏幕物理尺寸取值,图片尺寸 和 显示逻辑尺寸 * dpr(设备像素比) 取较小者即可。

屏幕逻辑像素和物理像素,以 iPhone 为参考如下:

设计切图尺寸若基于 750 作为 @1x 尺寸基准,如果不设置缓存尺寸,内存将会是设置了缓存尺寸的 3 倍 到 4 倍。

同样,BoxDecoration 的 Image 属性也需要设置缓存尺寸,为了提高开发效率,可考虑封装 Image 组件和 BoxDecoration 组件,实现自动按照布局尺寸去设置缓存尺寸。

ii. 图片资源裁剪

另外,network 图片在产生解码内存之前,会先将图片数据请求下来,获得一份二进制源图数据,即使图片解码完成,这份数据仍然留存在内存里,如图四所示。

图四

可以根据显示尺寸,利用图片服务的裁剪能力对图片尺寸进行裁剪,可以减少这部分的内存占用,也有利于提升加载效率和解码效率。

iii. 将图片缓存到本地

使用 cached_network_image 组件,可以将网络下载下来的图片缓存到本地,大幅度提升二次加载的效率。

iv. 针对 asset 图片做压缩处理

设计师切图一般给到的是 24 位 png 格式高清图片,可以使用 tinypng 工具进行手动压缩也可以使用 tinypng 提供的压缩服务,将 24 位压缩成 8 位以及删除一些不必要的元数据,压缩效果可达 50% 以上,虽是有损压缩,但是视觉上并无明显差异,是被设计师认可的压缩方式。

图五

减少图片数据内存增长,也有利于提升解码效率,还可以减少安装包大小。

v. 调整图片缓存阈值

了解下 ImageCache 对象(PaintingBinding.instance.imageCache):

缓存存储分为三种情况:请求处理中、使用中以及暂未使用图片缓存。

针对 _cache 的部分,内部实现了 LRU 机制,默认 100MB 或 1000 张图 满⾜其⼀,就标记最先缓存的对象给释放其引用。

可针对设备配置,适当降低缓存阈值,有助于降低 OOM 的概率,配合图片本地缓存,浏览体验不会有明显的影响。

vi. 样式图片和内容图片缓存隔离

我们可以将图片分为两大类,样式图片和内容图片:

样式图片:作为 APP 的 UI 风格的构成部分,通常被访问到的频率较高,作为样式的构成,我们一般不希望这种图片的加载存在用户能感知到的延迟,甚至有时候我们会选择提前预加载在缓存中。

内容图片:通常从接口获取,作为内容呈现给用户,用户习惯上可以容忍一定的加载延时。

对样式图片我们需要尽可能将高频访问的图片保留在内存中,而针对内容图片,我们可以选择更实时的方式去清理,然而框架自带的缓存机制对图片缓存的管理是无差别的。

解决方案是改造 ImageCache 类,加一个存储类型_assetsCache,存储 asset 类型图片缓存 ,需要的话也可以支持 LRU,指定缓存大小阈值。

在 _assetsCache 的基础上,我们可以高频的执行 _cache.clear() 来清理不再访问的缓存。

选择触发清理缓存的时机

  • 可以选择页面退出时触发,以及类弹框功能退出时触发。
  • 长列表无限加载场景,也可考虑滑窗实时创建和清理,同时在距离滑窗一定范围内对图片进行预加载,提高图片渲染效率。

2. 页面栈维度内存优化

用户长时间的浏览操作,在不同的页面之间穿梭,少不了持续不断的 push 页面到页面栈,随着页面不断地增加,内存也在持续增长。我们不得不考虑在页面栈的维度去做内存优化。

在原来的页面栈基础上,我们只需要保留顶层两个页面,第三层及以下的页面全部都被销毁回收内存。这种模式下,用户不断的打开新页面,内存也不会有明显的增长。

  • 当新打开一个页面,原来第二层的页面被执行销毁,回收该页面的所有内存。

图六

  • 当页面栈执行了 pop 操作,倒数第三层的页面变成第二层,开始执行页面重建,包括数据请求、Widget 树构建以及图片加载。

图七

  • 动态创建销毁页面的的方式,可能会丢失用户交互过程所产生的状态变更,影响用户体验。针对这种情况可以增加支持设置页面是否 KeepAlive,选择性地保留一些不好还原浏览状态的页面。

图八

当然,针对 KeepAlive 的页面,我们仍然可以执行对该页面图片缓存的强制清理。

六、总结

内存排查在引用链上寻找编码问题线索会有一定难度,需要多操作熟悉引用链的一些常见对象和展示规律,也可以尝试引入开源工具或自研工具来提升排查效率。

内存治理无法一蹴而就,需要提升对内存问题的警觉性,在编码细节上多留意强引用的释放时机,业务功能开发完后在转测前后去检查相关引用释放情况,确保避免内存随着浏览时间不断堆积。

MOO音乐的Flutter实战总结之内存治理(上)

MOO音乐的Flutter实战总结之内存治理(中)

QQ音乐招聘Android/ios客户端开发,点击左下方“查看原文”投递简历~

也可将简历发送至邮箱:tmezp@tencent.com

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯音乐技术团队 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档