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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:35:12