AVSpeechSynthesis与AVAudioSession切换连续中断4次触发IO错误问题
解决AVSpeechSynthesizer中断播报后切换监听的音频会话错误问题
这个问题我之前帮不少开发者排查过,核心是音频会话的状态没有被正确同步——用户主动中断播报时,AVSpeechSynthesizer的底层I/O操作可能还没完全停止,就急着切换到语音监听的会话配置,连续几次后就触发了系统的Deactivating an audio session that has running I/O错误。下面是具体的修复方案:
1. 中断播报时不要直接切换会话,等待合成器状态回调
不要在用户触发中断的瞬间(比如点击“停止”按钮)立刻修改AVAudioSession的状态,而是依赖AVSpeechSynthesizerDelegate的回调来处理后续操作,确保合成器完全停止后再切换:
// 确保你的音频管理类遵守AVSpeechSynthesizerDelegate class AudioManager: NSObject, AVSpeechSynthesizerDelegate { private let synthesizer = AVSpeechSynthesizer() private let audioSession = AVAudioSession.sharedInstance() override init() { super.init() synthesizer.delegate = self } // 用户触发中断播报的方法 func interruptSpeech() { if synthesizer.isSpeaking { // 只发送停止指令,不立刻处理会话 synthesizer.stopSpeaking(at: .immediate) } } // 合成器正常结束后的回调 func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) { switchToListeningMode() } // 合成器被中断取消后的回调 func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancelSpeechUtterance utterance: AVSpeechUtterance) { switchToListeningMode() } // 统一处理会话清理和启动监听 private func switchToListeningMode() { do { // 先停用播报的音频会话,确保I/O完全停止 try audioSession.setActive(false, options: .waitForCompletion) // 配置并激活监听用的会话 try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers) try audioSession.setActive(true) // 这里启动你的语音监听逻辑 startVoiceListening() } catch { print("音频会话切换错误: \(error.localizedDescription)") } } }
2. 启动播报前统一清理会话状态
每次启动语音播报前,也要确保监听的会话已经被正确停用,避免状态冲突:
func startSpeech(with content: String) { do { // 先停用监听会话(如果正在监听) try audioSession.setActive(false, options: .waitForCompletion) // 配置播报用的音频会话 try audioSession.setCategory(.playback, mode: .default, options: .duckOthers) try audioSession.setActive(true) let utterance = AVSpeechUtterance(string: content) utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN") synthesizer.speak(utterance) } catch { print("启动播报错误: \(error.localizedDescription)") } }
3. 关键原理说明
AVSpeechSynthesizer调用stopSpeaking(at:)后,底层的音频I/O不会立即停止,需要一点时间完成资源清理。如果在这个间隙强行切换或停用会话,系统会检测到仍有运行中的I/O操作,从而抛出错误。
通过代理回调等待合成器完全停止、使用.waitForCompletion选项强制等待I/O结束,这两个操作能确保会话状态完全同步,避免多次中断后的状态积累问题。
内容的提问来源于stack exchange,提问作者coco
相关产品推荐
相关产品推荐

