问题背景
文件列表中展示图片略缩图时,遇到以下几个典型问题:
- 批量上传完成后,文件列表立刻开始为新图片生成略缩图,导致 UI 卡顿。
- 批量删除过程中,进入视窗的图片也开始加载略缩图,进一步放大卡顿。
- 播放大相册时,滚动停下来的瞬间触发大量缩略图生成,CPU 峰值高。
目标是在保证略缩图体验的前提下,让滚动和操作流畅。
方案拆解
1)只在“滚动空闲”时加载
- 使用
NotificationListener<ScrollNotification>监听列表滚动状态:- 滚动开始/更新:标记
_scrollIdle = false; - 滚动结束后一段时间:通过防抖定时器把
_scrollIdle置回 true。
- 滚动开始/更新:标记
- 每个列表项/网格项的缩略图加载前先检查:
- 如果
scrollIdle == false,直接返回,不发起缩略图加载。
- 如果
这样可以避免在快速滚动时不断启动图片解码和缩放。
2)上传/删除期间暂停略缩图加载
- 在构建文件列表时,根据当前传输状态和删除状态计算
thumbsGate:- 如果存在正在运行/排队的上传任务,
thumbsGate = true; - 如果当前处于批量或单个删除流程,
thumbsGate = true。
- 如果存在正在运行/排队的上传任务,
- 下游列表项接收到的实际控制变量是:
scrollIdle && !thumbsGate。
效果:
- 批量上传/删除时,略缩图生成会被整体“拉闸”,直到这些重操作完成;
- 避免在设备最忙的时候还去做图片缩放,减轻卡顿。
3)增加硬性延时
- 即使滚动停下来,也不立刻加载缩略图:
- 当某个 item 变为可见,且
scrollIdle == true时,先启动一个定时器; - 等待一段时间(例如 800ms)后,如果该 item 仍然可见,再真正触发加载逻辑。
- 当某个 item 变为可见,且
- 可以过滤掉“稍微停一下又继续滚”的场景,进一步减少无谓开销。
4)缩略图生成放入 Isolate
- 使用
compute把image包的解码和缩放过程搬到后台 Isolate。 - 主 Isolate 的职责:
- 控制什么时候开始生成(上述滚动/gate/延时逻辑);
- 把生成好的
Uint8List放进内存缓存并刷新对应 item。
- 后台 Isolate 的职责:
- 完成 CPU 密集的 decode + resize + encode。
Isolate 的引入解决了一个关键问题:即使有少量缩略图在生成,也不会卡 UI。
收获
- 略缩图问题本质上是一个“何时做 + 在哪做”的问题:
- 何时做:通过滚动状态、上传/删除 Gate、硬性延时控制;
- 在哪做:通过 Isolate 把重计算移出主线程。
- 将两个维度都做好之后,即使在中低端设备上滚动大量图片列表,交互也能保持顺滑。