发现目前项目每次启动都要等待数据加载过程,如果遇到网络不佳时,等待的体验很不好,于是想到增加通过增加缓存来优化下体验。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

功能实现说明:首页数据缓存

本文档说明 App 首页数据缓存的实现:启动时优先展示上次缓存的列表数据,避免首屏空白,再在后台静默拉取最新数据并更新缓存。

在这里插入图片描述


一、目标与方案

  • 目标
    1. 启动 App 时,若存在上次成功加载的首页数据,先展示缓存内容,避免「无内容」的空白等待。
    2. 在展示缓存的同时或之后,后台拉取最新数据;拉取成功后更新列表并写入缓存,供下次启动使用。
    3. 设置页「清除缓存」同时清除首页数据缓存与图片内存缓存。
  • 方案
    • 新增 首页数据缓存服务HomeCacheService),使用 SharedPreferences 持久化作品列表(List<WorkModel> 的 JSON)。
    • 首页(HomePage)启动流程改为:先异步读取缓存 → 若有缓存则填充 _works / _images / _displayImages 并关闭 loading → 再调用原有 _loadData(silentIfHasCache: true) 做静默刷新;无缓存时仍为「先 loading 再拉取」。
    • 网络请求成功后,将本次解析得到的 _works 写入 HomeCacheService
    • 设置页「清除缓存」中增加调用 HomeCacheService().clear()

二、涉及文件

文件 说明
lib/services/home_cache_service.dart 新增。首页数据缓存服务:读/写/清除作品列表缓存。
lib/pages/home_page.dart 启动时先读缓存再拉取;_loadData 支持静默刷新并写入缓存。
lib/pages/profile_page.dart 「清除缓存」时同时清除首页数据缓存。

无新增 pub 依赖,沿用现有 shared_preferences


三、流程说明

3.1 首次启动(无缓存)

  1. initState_initWithCacheThenLoad()
  2. _homeCache.getCachedWorks() 返回 null
  3. 直接执行 _loadData(silentIfHasCache: false),全屏 loading,请求网络 → 解析 → 填充列表并 保存到缓存 → 关闭 loading。

3.2 再次启动(有缓存)

  1. initState_initWithCacheThenLoad()
  2. _homeCache.getCachedWorks() 返回上次的作品列表。
  3. 用缓存填充 _works_images,执行 _loadMore() 得到首屏 _displayImagessetState_isLoading = false立即展示缓存内容
  4. 执行 _loadData(silentIfHasCache: true):不置 _isLoading = true,在后台请求网络;成功则更新列表并再次写入缓存;失败仅保留当前缓存展示,不弹错误(用户可下拉刷新重试)。

3.3 下拉刷新 / 重试

  • 与原先一致:_refreshData() 清空展示并置 _isLoading = true,调用 _loadData()(不传 silentIfHasCache,即按「无缓存」流程),成功后同样会写入缓存。

四、实现要点

4.1 HomeCacheService

  • 存储键calm_harbor_home_works_cache
  • getCachedWorks() → 从 SharedPreferences 取 JSON 字符串,解析为 List<WorkModel>;异常或空则返回 null
  • saveWorks(List<WorkModel> works) → 将 works.map((w) => w.toJson()).toList()json.encode 后写入;空列表不写。
  • clear() → 移除该键,供设置页「清除缓存」使用。

单例,与 SettingsService 一样通过 SharedPreferences.getInstance() 获取实例。

4.2 HomePage 与缓存联动

  • _initWithCacheThenLoad()(在 initState 中调用):
    • await _homeCache.getCachedWorks()
    • 若缓存非空:从作品列表推导 _images(各作品 images 拼接),赋值 _works_images,清空 _displayImages、重置分页,_isLoading = false,再 await _loadMore() 得到首屏数据。
    • 最后统一 await _loadData(silentIfHasCache: cached != null && cached.isNotEmpty)
  • _loadData({bool silentIfHasCache = false})
    • silentIfHasCache == true 且当前 _works.isNotEmpty,则不执行 setState({ _isLoading = true }),实现静默刷新。
    • 网络成功、解析并赋值 _works / _images 后,调用 await _homeCache.saveWorks(_works),再 setState({ _isLoading = false })

4.3 设置页清除缓存

  • _clearCache() 中在原有 CacheService().clearImageCache() 之后增加 await HomeCacheService().clear(),一并清除首页数据缓存;提示文案仍使用原有「已清除缓存」/「清除缓存失败」。

五、关键代码

5.1 HomeCacheService 读写

Future<List<WorkModel>?> getCachedWorks() async {
  try {
    final prefs = await _store;
    final jsonStr = prefs.getString(_keyWorks);
    if (jsonStr == null || jsonStr.isEmpty) return null;
    final list = json.decode(jsonStr) as List<dynamic>?;
    if (list == null || list.isEmpty) return null;
    return list
        .map((e) => WorkModel.fromJson(Map<String, dynamic>.from(e as Map)))
        .toList();
  } catch (_) {
    return null;
  }
}

Future<void> saveWorks(List<WorkModel> works) async {
  if (works.isEmpty) return;
  try {
    final prefs = await _store;
    final list = works.map((w) => w.toJson()).toList();
    await prefs.setString(_keyWorks, json.encode(list));
  } catch (_) {}
}

5.2 首页先展示缓存再拉取

Future<void> _initWithCacheThenLoad() async {
  final cached = await _homeCache.getCachedWorks();
  if (!mounted) return;
  if (cached != null && cached.isNotEmpty) {
    final allImages = cached.fold<List<String>>([], (acc, w) => acc..addAll(w.images));
    if (allImages.isNotEmpty) {
      setState(() {
        _works = cached;
        _images = allImages;
        _displayImages.clear();
        _currentPage = 0;
        _hasMore = _images.isNotEmpty;
        _isLoading = false;
      });
      await _loadMore();
    }
  }
  await _loadData(silentIfHasCache: cached != null && cached.isNotEmpty);
}

5.3 静默刷新与写缓存

Future<void> _loadData({bool silentIfHasCache = false}) async {
  final hasCache = silentIfHasCache && _works.isNotEmpty;
  if (!hasCache) {
    setState(() { _isLoading = true; _errorMessage = null; });
  }
  try {
    // ... 网络请求、解析、赋值 _works / _images / _displayImages / _loadMore() ...
    await _homeCache.saveWorks(_works);
    if (!mounted) return;
    setState(() { _isLoading = false; });
  } catch (...) { ... }
}

六、注意事项

  • 缓存大小SharedPreferences 单键有大小限制(部分平台约 2MB),若作品数量或单条数据很大,可考虑改为文件缓存(如应用文档目录下的 JSON 文件)。
  • 数据一致性:缓存仅作「上次成功拉取的结果」快照,不保证与服务器实时一致;静默刷新失败时界面仍显示缓存,用户可通过下拉刷新重试。
  • 清除缓存:设置页「清除缓存」会同时清除图片内存缓存与首页数据缓存,下次启动将重新走「无缓存」流程,首屏会先出现 loading 再展示数据。

在这里插入图片描述

七、小结

通过 HomeCacheService 持久化首页作品列表,并在 HomePage 启动时「先读缓存再静默拉取」,实现了「启动即显示上次内容、后台更新」的体验;设置页清除缓存时一并清理首页数据缓存,与现有图片缓存逻辑统一。

结束语

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐