You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVAudioPlayer进入后台后currentTime返回值异常问题求助

解决AVAudioPlayer恢复播放后currentTime停止更新的问题

我之前也踩过AVAudioPlayer在后台/锁屏恢复时状态不同步的坑,跟你描述的问题一模一样——音频正常播放,但currentTime卡住,isPlaying还返回false,而且这个bug确实偶发,得重复操作几次才会触发,挺头疼的。

问题根源

这是AVAudioPlayer的一个已知状态同步bug,当App从后台/锁屏恢复,或者音频中断结束后,AVAudioPlayer的内部状态没有跟上系统音频流的实际状态,导致它对外暴露的currentTime和isPlaying属性失效,但底层的音频播放其实还在正常进行。

可行的解决方案

方案1:监听音频会话和App状态,强制同步播放器状态

通过监听音频会话中断通知和App前后台切换事件,在状态恢复时手动同步AVAudioPlayer的状态,强制它更新内部属性:

首先注册相关通知(建议在初始化时添加):

override init() {
    super.init()
    // 监听音频会话中断
    NotificationCenter.default.addObserver(self, selector: #selector(handleAudioSessionInterruption(_:)), name: AVAudioSession.interruptionNotification, object: AVAudioSession.sharedInstance())
    // 监听App前后台切换
    NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}

然后实现通知处理逻辑:

@objc private func handleAudioSessionInterruption(_ notification: Notification) {
    guard let info = notification.userInfo,
          let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
          let interruptionType = AVAudioSession.InterruptionType(rawValue: typeValue) else {
        return
    }

    // 中断结束,尝试恢复播放并同步状态
    if interruptionType == .ended {
        guard let optionValue = info[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
        let options = AVAudioSession.InterruptionOptions(rawValue: optionValue)
        
        if options.contains(.shouldResume) {
            do {
                try AVAudioSession.sharedInstance().setActive(true)
                // 强制同步:如果播放器状态显示未播放,但实际应该在播放,先暂停再播放
                if !player.isPlaying && playbackState == .playing {
                    player.pause()
                    player.play()
                }
                setTimer(true) // 确保Timer正常运行
            } catch {
                print("激活音频会话失败:\(error.localizedDescription)")
            }
        }
    }
}

@objc private func appDidEnterBackground() {
    // 后台时确保音频会话处于激活状态
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playback, mode: .default, options: .mixWithOthers)
        try session.setActive(true)
    } catch {
        print("后台音频会话配置失败:\(error.localizedDescription)")
    }
}

@objc private func appWillEnterForeground() {
    // 前台恢复时检查状态一致性
    if !player.isPlaying && playbackState == .playing {
        player.pause()
        player.play()
        setTimer(true)
    }
}

方案2:自行维护播放时间,不依赖AVAudioPlayer的currentTime

如果不想跟AVAudioPlayer的状态bug较劲,可以完全自己计算播放时长,彻底摆脱对它的currentTime依赖:

添加几个变量来记录播放状态:

private var accumulatedPlayTime: TimeInterval = 0 // 累计播放时长
private var lastPlayStartTime: Date? // 上次开始播放的时间戳

修改play()和pause()方法:

func play() {
    print("Tries playing")
    guard player.prepareToPlay(), player.play() else {
        playbackState = .failed
        return
    }
    print("Playing OK")
    // 记录开始播放的时间
    lastPlayStartTime = Date()
    setTimer(true)
    playbackState = .playing
}

func pause() {
    player.pause()
    // 累加播放时长
    if let startTime = lastPlayStartTime {
        accumulatedPlayTime += Date().timeIntervalSince(startTime)
        lastPlayStartTime = nil
    }
    setTimer(false)
    playbackState = .paused
}

修改Timer的回调逻辑,用自己计算的时间替代player.currentTime:

timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in
    var currentPlayTime = self.accumulatedPlayTime
    // 如果正在播放,加上从上次开始到现在的时长
    if let startTime = self.lastPlayStartTime {
        currentPlayTime += Date().timeIntervalSince(startTime)
    }
    self.currentPlaybackTime = currentPlayTime
    print("Delegate newTime: \(currentPlayTime)")
})

这个方案的优势是完全绕过了AVAudioPlayer的状态问题,时间计算更可靠,适合对播放进度精度要求高的场景。

额外注意事项

  • 确保你的音频会话类别设置为.playback,并且开启了后台播放权限(在Info.plist中添加UIBackgroundModes并包含audio)
  • 避免在后台频繁调用AVAudioPlayer的属性,可能会加剧状态同步问题

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:23:50