使用AVAudioEngine恢复播放时App卡顿及音频抢占需求求助
解决音频抢占卡顿问题,实现类似Apple Music的独占播放
核心问题分析
当前代码的音频会话未明确声明独占播放意图,导致系统切换音频时需额外处理混音逻辑,引发700-800ms卡顿;同时播放操作时才启动AVAudioEngine,进一步增加了延迟。
解决方案步骤
1. 调整音频会话配置,明确抢占行为
修改音频会话设置,激活时通知其他应用暂停音频,确保独占播放:
func startAudioSession() { let session = AVAudioSession.sharedInstance() do { // 设置playback类别,禁用混音 try session.setCategory(.playback, mode: .default, options: []) // 激活会话并通知其他应用暂停音频 try session.setActive(true, options: .notifyOthersOnDeactivation) } catch { print("Failed to start Audio Session: \(error.localizedDescription)") } }
2. 提前初始化音频引擎,消除启动延迟
将音频引擎启动提前到初始化阶段,避免播放时的启动开销:
private init() { startAudioSession() setupAudioEngine() setupNotifications() // 提前启动音频引擎 do { try audioEngine.start() } catch { print("Failed to start audio engine: \(error.localizedDescription)") } }
3. 优化播放/暂停逻辑,减少操作延迟
修改播放方法,仅控制播放器节点状态,无需重复启停引擎:
func play() { if playerNode.isPlaying { playerNode.pause() isPlaying = false } else { // 再次确认会话激活,确保抢占生效 do { try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation) } catch { print("Failed to reactivate audio session: \(error.localizedDescription)") } playerNode.play() isPlaying = true } }
4. 修复中断处理逻辑
修正中断响应逻辑,避免重复触发播放/暂停:
@objc func handleInterruption(notification: Notification) { guard let userInfo = notification.userInfo, let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { return } switch type { case .began: print("Interruption Began") // 中断开始时暂停播放 if playerNode.isPlaying { playerNode.pause() isPlaying = false } case .ended: guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return } let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue) if options.contains(.shouldResume) { print("Resuming playback after interruption") playerNode.play() isPlaying = true } default: () } }
5. 修正状态管理同步问题
将播放状态直接整合到AudioManager中,确保UI与音频状态同步:
class AudioManager: ObservableObject { static let shared = AudioManager() private let audioEngine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() // 直接管理播放状态 @Published var isPlaying = false private init() { startAudioSession() setupAudioEngine() setupNotifications() do { try audioEngine.start() } catch { print("Failed to start audio engine: \(error.localizedDescription)") } } // 其他方法保持修改后的逻辑 } // 修改ContentView struct ContentView: View { @EnvironmentObject var audioManager: AudioManager var body: some View { Button { audioManager.play() } label: { Image(systemName: audioManager.isPlaying ? "pause.fill" : "play.fill") .font(.title) .foregroundStyle(.white) } } } // 在App入口注入AudioManager @main struct YourApp: App { var body: some Scene { WindowGroup { ContentView() .environmentObject(AudioManager.shared) } } }
关键说明
.notifyOthersOnDeactivation选项会在激活当前会话时,立即通知其他应用暂停音频,实现无延迟抢占。- 提前启动
AVAudioEngine消除了播放时的引擎启动开销,彻底解决卡顿问题。 - 整合后的状态管理确保UI与音频播放状态完全同步。
内容的提问来源于stack exchange,提问作者User95797654974
相关产品推荐
相关产品推荐

