目前项目主要是网络图片的加载,发现每次图片都存在重复多次的情况,体验上有问题,现在下手看看如何优化。
文章目录

优化图片缓存与占位图
这里讨论的是与图片相关的性能优化实现:网络图片内存缓存 (单例 Dio + LRU)、加载中/失败占位图,以及与设置页「清除缓存」的联动。
一、目标与方案
- 目标 :
- 同一图片 URL 只下载一次,避免重复请求与流量浪费。
- 使用单例 Dio 做图片请求,减少连接数、提升 HarmonyOS 等平台稳定性。
- 加载中与加载失败时展示统一、可定制的占位图,提升体验。
- 设置页「清除缓存」同时清除本应用的图片内存缓存。
- 方案 :
- 新增 图片缓存服务 (
ImageCacheService):单例 Dio + 内存 LRU 缓存(最多 80 张),请求去重(同一 URL 并发只发一次)。 - NetworkImageWidget 改为通过
ImageCacheService取图;支持可选placeholder(加载中)、errorPlaceholder(失败);未传时使用资源图片asset/image/placeholder.png、asset/image/errorPlaceholder.png作为默认占位图。 - CacheService 在清除 Flutter ImageCache 时同时调用
ImageCacheService().clearMemoryCache()。
- 新增 图片缓存服务 (
二、涉及文件
| 文件 | 说明 |
|---|---|
lib/services/image_cache_service.dart |
新增。图片缓存服务:单例 Dio、LRU 内存缓存、请求去重、清除接口。 |
lib/widgets/network_image_widget.dart |
重构 。使用缓存服务取图;新增 placeholder / errorPlaceholder;默认占位使用资源图片。 |
lib/services/cache_service.dart |
清除缓存时增加 ImageCacheService().clearMemoryCache()。 |
pubspec.yaml |
注册资源:asset/image/placeholder.png、asset/image/errorPlaceholder.png。 |
asset/image/placeholder.png |
加载中占位图资源。 |
asset/image/errorPlaceholder.png |
加载失败占位图资源(如「无法加载图片」图示)。 |
三、实现说明
3.1 ImageCacheService(图片缓存服务)
- 单例 Dio:构造时创建一次,所有图片请求共用,统一超时与 UA(HarmonyOS 兼容)。
- 内存缓存 :
Map<String, Uint8List>+List<String>做 LRU;最多保留_maxCacheCount = 80条,超出时淘汰最久未访问的 URL。 - 请求去重 :
Map<String, Future<Uint8List?>> _inFlight;同一 URL 多次调用getImage时共用一个 Future,避免重复下载。 - 接口 :
getImage(String url):先查缓存 → 再查进行中请求 → 否则发起下载并写入缓存。clearMemoryCache():清空_cache与_lruKeys,供设置页「清除缓存」使用。
3.2 NetworkImageWidget(网络图片组件)
- 数据来源 :
ImageCacheService().getImage(imageUrl),不再为每张图新建 Dio。 - 占位图 :
- 加载中 :
placeholder为 null 时使用资源asset/image/placeholder.png,与主图一致使用fit填充。 - 加载失败 :
errorPlaceholder为 null 时使用资源asset/image/errorPlaceholder.png(如「无法加载图片」图示),与主图一致使用fit填充。 - 调用方可通过
placeholder/errorPlaceholder传入自定义 Widget,覆盖默认资源图。
- 加载中 :
- 生命周期 :
didUpdateWidget中若imageUrl变化则重新加载;dispose前通过_disposed避免异步回调中setState。 - 移除 :原每图
print日志已去掉,减少生产环境开销。
3.3 CacheService(清除缓存)
clearImageCache()在原有PaintingBinding.instance.imageCache的clear/clearLiveImages之后,增加ImageCacheService().clearMemoryCache(),保证设置页「清除缓存」会一并清掉应用内图片内存缓存。
四、使用方式
准备 placeholder.png和errorPlaceholder.png 素材,引入到
/asset/image/errorPlaceholder.png
/asset/image/placeholder.png


dart
NetworkImageWidget(
imageUrl: imageUrl,
fit: BoxFit.cover,
placeholder: Container(
color: Colors.grey[800],
child: Center(child: CircularProgressIndicator(color: Colors.white)),
),
errorPlaceholder: Container(
color: Colors.grey[800],
child: Center(child: Text('图片加载失败', style: TextStyle(color: Colors.white70))),
),
)
首页、发现页、收藏页、详情页中已有的 NetworkImageWidget 均无需改参数即可获得缓存与默认占位图;若需统一为自定义占位图,可在各页传入相同 placeholder / errorPlaceholder。
五、效果与注意点
- 效果 :
- 同一 URL 只下载一次,列表/详情来回切换时图片从内存缓存读取,加载更快、流量更省。
- 单例 Dio 减少连接数,有利于弱网与 HarmonyOS 环境。
- 加载中/失败有统一占位,避免空白或杂乱布局。
- 注意 :
- 缓存仅内存、不落盘,进程退出后重新下载;缓存条数 80,可根据真机内存情况在
ImageCacheService._maxCacheCount调整。 - 设置页「清除缓存」会清空该 LRU 缓存与 Flutter 图片缓存,下次进入列表会重新拉取图片。
- 缓存仅内存、不落盘,进程退出后重新下载;缓存条数 80,可根据真机内存情况在

结束语
感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区: