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

如何正确处理AVAudioEngine抢占其他音频应用会话时的卡顿问题

解决AVAudioEngine抢占音频会话时的卡顿问题

核心问题分析

你的卡顿主要来自两个点:

  1. 音频会话激活逻辑不完善,抢占其他应用音频时没有正确处理系统交互流程
  2. 过早启动AVAudioEngine且依赖重启引擎恢复播放,触发了系统层面的资源重载延迟

针对性解决方案

1. 优化音频会话抢占逻辑

修改音频会话激活代码,明确抢占行为并处理错误,避免静默失败:

func activateAudioSession() {
    let session = AVAudioSession.sharedInstance()
    do {
        // 设置播放类别,确保后台播放优先级
        try session.setCategory(.playback, mode: .default)
        // 激活会话并抢占其他应用音频,同时通知其他应用暂停
        try session.setActive(true, options: .notifyOthersOnDeactivation)
    } catch {
        print("音频会话激活失败: \(error.localizedDescription)")
    }
}

2. 避免过早启动AVAudioEngine

移除init中的audioEngine.start(),改为按需启动引擎,减少不必要的初始化开销:

func startAudioEngineIfNeeded() {
    guard !audioEngine.isRunning else { return }
    do {
        try audioEngine.start()
    } catch {
        print("音频引擎启动失败: \(error.localizedDescription)")
    }
}

3. 重构播放按钮点击逻辑

点击播放时按「激活会话→启动引擎→恢复播放」的顺序执行,确保每一步资源就绪:

func triggerPlay() {
    activateAudioSession()
    startAudioEngineIfNeeded()
    playerNode.play()
    musicPlayerControlsManager.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("中断开始")
        musicPlayerControlsManager.isPlaying = false
        playerNode.pause()
    case .ended:
        print("中断结束")
        guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
        let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
        
        if options.contains(.shouldResume) {
            activateAudioSession()
            startAudioEngineIfNeeded()
            musicPlayerControlsManager.isPlaying = true
            playerNode.play()
        } else {
            musicPlayerControlsManager.isPlaying = false
        }
    default: break
    }
}

5. 新增蓝牙设备路由适配

监听路由变化,确保AirPods等设备切换后音频流畅恢复:

func setupNotifications() {
    let nc = NotificationCenter.default
    nc.addObserver(self, selector: #selector(handleInterruption), name: AVAudioSession.interruptionNotification, object: AVAudioSession.sharedInstance())
    nc.addObserver(self, selector: #selector(handleRouteChange), name: AVAudioSession.routeChangeNotification, object: AVAudioSession.sharedInstance())
}

@objc func handleRouteChange(notification: Notification) {
    guard let userInfo = notification.userInfo,
          let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
          let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { return }
    
    switch reason {
    case .newDeviceAvailable, .oldDeviceUnavailable:
        if musicPlayerControlsManager.isPlaying {
            startAudioEngineIfNeeded()
            playerNode.play()
        }
    default: break
    }
}

关键注意事项

  • 绝对避免重启AVAudioEngine,优先通过pause()/play()控制节点状态
  • 所有音频相关操作尽量放在后台线程执行,避免阻塞主线程
  • 不要忽略try?隐藏的错误,必须捕获并处理音频会话和引擎的启动异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:33:30