Skip to content

feat(android,web): 实现本地缓存与预加载优化,修复多项体验问题 - #24

Merged
Re-BeiChen merged 2 commits into
SPlayer-Dev:devfrom
Shomi-FJS:cache-fix
May 23, 2026
Merged

Re-BeiChen merged 2 commits into
SPlayer-Dev:devfrom
Shomi-FJS:cache-fix

Conversation

@Shomi-FJS

Copy link
Copy Markdown

📌 变更类型

  • ✨ feat — 新功能
  • 🐞 fix — Bug 修复
  • 🎨 style — 仅样式 / 格式,不影响逻辑
  • ♻️ refactor — 重构,不改变外部行为
  • ⚡ perf — 性能优化
  • 📝 docs — 仅文档
  • 🧪 test — 新增 / 修改测试
  • 🔧 chore — 构建 / 脚本 / 依赖
  • 🚀 ci — 仅 CI 配置
  • ⏪ revert — 回滚

📝 变更说明

本次提交是一次围绕「Android 端缓存与播放体验」的综合升级,跨 Vue 渲染层、Capacitor 桥层、Android 原生层三端协同:

1. 原生缓存基础设施(新增)

  • 新增 Android 原生缓存插件:封面 / 歌词 / 列表数据 全部走本地 LRU;支持自定义缓存上限(最低 256 MB,可设小数)与自动驱逐最久未访问条目
  • 设置面板新增「缓存大小上限 + 总占用统计 + 分项清理」入口,实时显示设备剩余空间与生效上限
  • 流媒体(Jellyfin / Navidrome 等)场景接入本地缓存兜底,离线也可查看已缓存歌单详情

2. 图片加载缓存化

  • SImage 组件新增 cacheType 参数,按调用点声明缓存域(covers / list-covers / none),命中后复用 Blob URL,减少重复网络请求与首屏抖动
  • 内存级 LRU 引用计数:memoryHitGet/Put/Release 三态生命周期,配合卸载时主动 release,避免内存泄漏

3. 音频缓存与预加载

  • ExoPlayer SimpleCache 接入:所有 http(s) 音频统一走 CacheDataSource;resolveCacheKey 提取音频文件名作为稳定 key,跨签名 URL 也能命中
  • 切歌秒级响应:下一首音频预下载首 512 KB,命中后 ExoPlayer 直接读盘
  • promotion 机制:用户连续播放 ≥ 10 秒视为「真喜欢」,触发完整下载并对照 Content-Length 严格校验后标记 promoted(automix 与离线场景的可信源)

4. 歌词渲染性能

  • 使用 requestIdleCallback 异步构建歌词动画,避免在切歌热路径阻塞主线程

5. Bug 修复

  • 修复歌单 / 电台序列化 O(N²) 性能问题(首次必存 + ≥ 2 s 节流 + 末页必存)
  • 修复歌单续传:partial cache 不再触发全量重拉,从 cached.songs.length 偏移续传
  • 修复 Android 后台 ENDED 续播跳过首曲:末尾 ALL wrap 时改用 current(),常规右滑窗口仍走 advanceRaw(false)
  • 修复原生切歌后 currentAudioSource 滞留导致下一首 URL 推错
  • 修复封面 pending download 共享 Promise 的引用计数泄漏(多个组件 release 同一份 retain)
  • 修复 prefetch 截断文件被错误 promote 的隐患(promote 前比对 cache.getCachedBytes vs ContentMetadata.getContentLength)
  • 收紧 allowBackup 备份范围:排除 WebView 存储 / 登录 cookie / Capacitor SharedPreferences,避免跨设备恢复泄露账号会话

🔗 关联 Issue

Closes #

📱 影响范围

  • 🎵 播放引擎 / 音频
  • 📝 歌词 / 桌面歌词
  • 🔔 通知栏 / MediaSession
  • 🌐 在线音乐 (网易云 / Jellyfin / Navidrome / Emby / Subsonic / Last.fm)
  • 🧩 内置 API (nodejs-mobile)
  • 🎨 UI / 主题 / 布局
  • 📦 构建 / 打包 / 签名
  • 📱 Capacitor / 原生 Android 代码
  • 📄 文档 / README

✅ 自检清单

  • 本 PR 目标分支为 dev
  • 本地已执行 pnpm lint 且无 warning
  • 本地已执行 pnpm typecheck 且无报错
  • 已在 至少一台真机 上构建并验证关键路径
  • UI 改动已兼顾 手机竖屏 + 平板横屏 两种布局
  • 新增 / 修改的文案使用中文,与项目整体风格一致
  • 未引入不必要的依赖 / 大体积资源
  • 未修改签名密钥 / CI Secrets 相关文件

📸 截图 / 录屏

改动前 改动后
(缓存设置面板缺失) (新增上限/占用/分项清理)
(切歌等待 1-3 s) (秒级响应,预载命中无网络)
(末尾循环跳第 0 首) (正确从 track 0 开始)

🧪 测试方式

  1. 缓存基础:进入「设置 → 本地缓存」,调整上限到 256 MB,播放并查看「缓存总占用」实时变化;点「清空音频缓存」后再播放确认重新落盘
  2. 图片缓存:进入歌单 / 专辑详情查看封面,断网后重新打开同一页,封面仍从本地 Blob URL 加载(不再有占位抖动)
  3. 音频预载:连续播放两首,第二首点击 → 立即起播无 buffering(adb logcat 可见 prefetch done);同一首再播一遍,无 HTTP upstream 日志
  4. 续传:进入超长歌单(>1000 首),未加载完即返回,再次进入应在已有数据基础上接着拉,不丢列表
  5. 后台播放:锁屏 / 切到其他应用,自动播下一首时通知栏标题、按键状态正确同步
  6. 隐私:使用 adb backup -f x.ab <pkg> 后用 abe.jar 解包,确认 app_webview/ 与 CapacitorStorage.xml 不在备份内容中

💬 其他说明

  • 跨语言契约:updateQueueContext 新增 windowResetFromWrap 字段,JS / Java 同步落地;旧版混用时降级为默认 false,行为退回原 advanceRaw 路径(不致命)
  • promote 严格校验 仅在 upstream 提供 Content-Length 时生效;chunked encoding 等无长度场景按现行 EOF 行为信任,符合 best-effort 语义
  • 缓存键策略:当前以文件名末段为主键,覆盖网易云 / 多数 CDN;若后续接入特殊 CDN(无标准音频后缀),可在 resolveCacheKey 的回退分支扩展

Shomi-FJS added 2 commits May 23, 2026 02:02
本次提交完成多维度功能升级与问题修复:
1.  新增Android端原生缓存插件,实现封面、歌词、列表数据的本地LRU缓存管理,支持自定义缓存上限与自动清理
2.  全量接入缓存驱动的图片加载:为SImage新增cacheType参数,自动复用本地缓存Blob URL,减少网络请求与加载抖动
3.  实现音频预加载优化:Android端预下载下一首音频前512KB,切歌秒级响应
4.  优化歌词渲染性能:使用requestIdleCallback异步构建歌词动画,避免主线程阻塞
5.  修复多项已知问题:
    - 修复播放历史列表序列化O(N²)性能问题
    - 修复歌单续传加载逻辑,避免全量重拉已有数据
    - 修复Android后台播放队列重置时跳过首曲的BUG
6.  新增缓存设置面板,支持分项清理与实时占用统计
7.  流媒体场景新增本地缓存兜底,离线也可查看已缓存歌单
将原有的播放音质和音源的推断逻辑抽离为inferNativePlaybackInfo私有方法,
简化prepareSongToPlay和applyNativeTrackChanged两处重复代码,统一播放信息的处理逻辑。
@Re-BeiChen
Re-BeiChen merged commit 8e4e9a0 into SPlayer-Dev:dev May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants