AVPlayer addPeriodicTimeObserverForInterval方法持续调用不停止问题排查
结合你提供的代码和问题描述,这种playingSeconds远超歌曲实际时长的情况,主要是因为AVPlayer的周期性时间观察者没有按预期停止,或者播放状态的判断逻辑存在漏洞。下面是几个最可能的原因及排查方向:
1. 时间观察者未被正确移除
AVPlayer的addPeriodicTimeObserverForInterval:方法返回的观察者对象,必须在不需要时主动调用removeTimeObserver:销毁。如果你的播放器在以下场景中没有移除观察者:
- 歌曲自然播放完毕后
- 手动暂停/停止播放时
- 切换到下一首歌曲时
- 播放器实例被释放前
旧的观察者会一直监听时间变化,哪怕歌曲已经结束,甚至播放器被复用。极端情况下如果多次添加观察者而不移除,还会出现多个回调同时执行,导致playingSeconds被多次累加。
排查建议:
在playerItem的AVPlayerItemDidPlayToEndTimeNotification回调、暂停/停止方法中,添加移除观察者的代码:
if (self.playbackTimeObserver) { [self.player removeTimeObserver:self.playbackTimeObserver]; self.playbackTimeObserver = nil; }
2. 播放状态self.state未正确更新
你的代码中只有当self.state == BPAudioPlayerStatePlaying && !self.isBuffering时,才会累加playingSeconds。如果歌曲播放完毕后,self.state没有被正确切换为非播放状态(比如BPAudioPlayerStateStopped或BPAudioPlayerStatePaused),这个判断条件会一直为真,导致回调持续执行、playingSeconds疯涨。
常见的异常场景:
- 单曲循环逻辑中,歌曲结束后没有先更新状态再重新播放
- 播放器遇到解码错误或资源异常,但未触发状态变更
- 后台播放恢复时,状态同步失败
排查建议:
监听AVPlayerItemDidPlayToEndTimeNotification,在回调中强制更新播放状态,并根据循环逻辑决定是否重新播放:
- (void)playerItemDidFinishPlaying:(NSNotification *)notification { self.state = BPAudioPlayerStateStopped; // 处理单曲循环逻辑 if (self.isLoop) { [self.player seekToTime:kCMTimeZero]; self.state = BPAudioPlayerStatePlaying; [self.player play]; } // 移除旧观察者,循环播放时重新添加(如果需要) [self removeTimeObserver]; if (self.isLoop) { [self addTimeObserver]; } }
3. playerItem的时间参数异常
代码中计算totalDuration和currentTime时,没有判断CMTime的有效性:
- 如果
playerItem.duration是kCMTimeInvalid(比如资源加载失败、网络错误),self.totalDuration会被错误设置为0,导致ct <= self.totalDuration的判断失效 - 如果
currentTime出现异常值(比如无限循环的时间戳),会绕过时长判断,持续触发累加逻辑
排查建议:
在计算时长前先判断CMTime的有效性:
if (self.totalDuration == 0 && CMTIME_IS_VALID(self.playerItem.duration)) { self.totalDuration = self.playerItem.duration.value / self.playerItem.duration.timescale; [self updateNowPlayingInfo:YES]; }
同时给playingSeconds的累加加一个兜底判断:
if (self.state == BPAudioPlayerStatePlaying && !self.isBuffering) { if (ct <= 0) { self.playingSeconds = 0; } else if (self.playingSeconds < self.totalDuration) { // 兜底:不超过歌曲总时长 self.playingSeconds += 1; } }
4. 后台播放或系统中断后的状态异常
如果你的APP支持后台播放,以下场景可能导致观察者持续运行:
- 后台播放时,音频会话被系统临时占用(比如来电、 Siri),恢复后播放器状态未同步
- APP进入后台后,AVPlayer的时间观察者没有被正确暂停,仍在后台执行回调
排查建议:
监听音频会话的状态变化(AVAudioSessionInterruptionNotification),在中断开始时移除观察者,中断结束后根据状态重新添加:
- (void)handleAudioSessionInterruption:(NSNotification *)notification { NSDictionary *info = notification.userInfo; AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; if (type == AVAudioSessionInterruptionTypeBegan) { [self removeTimeObserver]; } else if (type == AVAudioSessionInterruptionTypeEnded) { if (self.state == BPAudioPlayerStatePlaying) { [self addTimeObserver]; [self.player play]; } } }
内容的提问来源于stack exchange,提问作者Carry

