Media3 PlayerManager 的音源解析、队列管理与失败恢复机制
播放核心架构
NeriPlayer 的播放由 PlayerManager 作为唯一协调者,底层基于 Android Media3 ExoPlayer 引擎。PlayerManager 统一管理:
- 音源解析:按平台标签(Bilibili / Netease / YouTube / 本地)分发到对应解析器
- 队列管理:随机播放生成真实乱序队列后持久化和展示
- 状态恢复:上次播放进度、循环/随机模式在应用重启后恢复
- 失败重试:刷新播放链接、音质降级、平台间兜底
- YouTube 预取:优先缓存 bootstrap / player.js / PoToken 结果
音源解析分发逻辑
resolveSongUrl(song)
├─ 优先本地缓存命中(不经过任何平台接口)
├─ song.album 以 "Bilibili" 开头 → getBiliAudioUrl(avid, cid)
├─ song.album 以 "Netease" 开头 → getNeteaseSongUrl(songId)
├─ song.album 以 "YouTube" 开头 → getYouTubeAudioUrl(videoId)
└─ 其余 → 本地文件播放
每个平台返回的 SongItem 都会在 album 字段前加上平台标签,播放器据此判断调用哪个平台的 API。
队列与随机
随机播放不是"每次抽一首"——PlayerManager 会先生成一条真实乱序队列,然后按该队列顺序播放、持久化和展示。这样用户看到的"随机播放列表"就是实际的播放顺序,不会出现同一首歌连续出现或部分歌永远轮不到的情况。
长音频进度记忆
长音频进度记忆默认开启,规则:
- 仅对时长 ≥ 15 分钟的内容保存位置
- 低于 5 秒的位置不写入
- 距离结尾 30 秒以内视为已完成并清零
- 显式指定的播放位置优先于记忆位置
该位置以 resumePositionMs 参与本地与可选同步,不代表第三方平台的播放历史。
预抢占音频焦点
播放器支持预抢占音频焦点,避免系统把 NeriPlayer 当作低优先级播放者被后台杀掉。