fix(playback): 网络切换重连不再抢在切歌前面

日志(21:51,用户刚把手机切到有线):
  16.828 尝试重连:为爱痴狂(从 78s 继续)  ← 网络变化触发,但用户已点新歌
  17.569 play(新歌)
  18.128 尝试重连:Communications(从 0s)  ← 两条流程交叠
  23.669 Failed to open -> 30.459 起播 5568ms

- _onNetworkChanged:去抖开始时记录 songId + playbackGeneration,
  2s 后若已切歌则忽略本次重连
- _recoverFromStall:await 打开完成后校验 generation,
  切歌了就放弃本次恢复,避免把旧歌重新打开
This commit is contained in:
2026-10-31 21:54:15 +08:00
parent b439d1cb8c
commit 5c4b0c2728
+16
View File
@@ -649,6 +649,9 @@ class AudioService extends ChangeNotifier {
} }
_lastStallRecoveryAt = now; _lastStallRecoveryAt = now;
// 恢复前记下代次:await 期间用户可能已经切歌,
// 不能把旧歌重新打开(否则就是"点了新歌却切回旧歌")。
final recoveryGeneration = _playbackGeneration;
final resumeAt = _lastKnownPosition > Duration.zero final resumeAt = _lastKnownPosition > Duration.zero
? _lastKnownPosition ? _lastKnownPosition
: positionNotifier.value; : positionNotifier.value;
@@ -659,6 +662,10 @@ class AudioService extends ChangeNotifier {
_playReadyAt = null; _playReadyAt = null;
_firstAudioReported = false; _firstAudioReported = false;
await _playWithHeaders(url); await _playWithHeaders(url);
if (_playbackGeneration != recoveryGeneration) {
debugPrint('⏭️ [AudioService] 恢复期间已切歌,放弃本次恢复');
return;
}
_playReadyAt = DateTime.now(); _playReadyAt = DateTime.now();
if (resumeAt > Duration.zero) { if (resumeAt > Duration.zero) {
await PlaybackService().seek(resumeAt); await PlaybackService().seek(resumeAt);
@@ -681,9 +688,18 @@ class AudioService extends ChangeNotifier {
/// 这里做 3s 去抖:切换瞬间往往会连续触发多次网络接口变化。 /// 这里做 3s 去抖:切换瞬间往往会连续触发多次网络接口变化。
void _onNetworkChanged() { void _onNetworkChanged() {
_networkDebounce?.cancel(); _networkDebounce?.cancel();
// 记录去抖开始时的歌:2 秒内用户可能已经切歌,
// 那时再去重连"上一首"就是异常切换(日志里出现过 78s 重连旧歌)。
final expectedSongId = _currentSong?.id;
final expectedGeneration = _playbackGeneration;
_networkDebounce = Timer(const Duration(seconds: 2), () { _networkDebounce = Timer(const Duration(seconds: 2), () {
if (_currentSong == null) return; if (_currentSong == null) return;
if (_isChangingTrack) return; if (_isChangingTrack) return;
if (_currentSong?.id != expectedSongId ||
_playbackGeneration != expectedGeneration) {
debugPrint('🌐 [AudioService] 网络变化期间已切歌,忽略本次重连');
return;
}
debugPrint('🌐 [AudioService] 网络已切换,主动重连当前歌曲'); debugPrint('🌐 [AudioService] 网络已切换,主动重连当前歌曲');
_recoverFromStall(force: true); _recoverFromStall(force: true);
}); });