为何在speechSynthesizer代理中调用audioSession.setActive(false)总是失败?
AVAudioSession setActive(false) 在语音合成完成后调用失败的原因与解决办法
核心原因分析
- 语音合成资源未完全释放:
speechSynthesizer(_:didFinish:)回调触发时,AVSpeechSynthesizer的底层音频处理线程可能还没彻底结束,此时音频会话仍被系统标记为“在使用中”,直接调用setActive(false)会被拒绝。 - 错误处理缺失:初始化和激活会话时使用
try?吞掉了所有错误,可能会话本身就没成功激活,后续的停用操作自然会失败。 - 停用选项未正确设置:默认调用
setActive(false)时,系统不会通知其他音频应用恢复播放,部分场景下系统会阻止会话停用。
具体解决办法
- 延迟执行停用操作:给底层资源留够释放时间,比如延迟0.5秒再调用停用方法。
- 完善全链路错误处理:替换所有
try?为do-catch,排查会话初始化、激活阶段的问题。 - 添加停用选项:调用
setActive(false, options: .notifyOthersOnDeactivation),明确通知其他应用恢复音频。 - 保证激活/停用配对:避免多次调用
setActive(true)但只调用一次setActive(false),可以用计数器跟踪激活次数。
修改后的示例代码
import Foundation import AVFoundation class Speaker: NSObject, AVSpeechSynthesizerDelegate, @unchecked Sendable { static let shared = Speaker() let audioSession = AVAudioSession.sharedInstance() let synthesizer = AVSpeechSynthesizer() private var activationCount = 0 // 跟踪会话激活次数 override init() { super.init() synthesizer.delegate = self do { try audioSession.setCategory(.playback, options: [.duckOthers]) } catch { print("会话类别设置失败:\(error.localizedDescription)") } } func speakChat(text: String) { let utterance = AVSpeechUtterance(string: text) utterance.rate = 0.5 utterance.volume = 1.0 activationCount += 1 do { try audioSession.setActive(true) } catch { print("会话激活失败:\(error.localizedDescription)") activationCount -= 1 } synthesizer.speak(utterance) } func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { activationCount -= 1 // 只有当激活次数归0时才停用会话 guard activationCount <= 0 else { return } // 延迟0.5秒执行,确保底层资源释放 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in guard let self = self else { return } do { try self.audioSession.setActive(false, options: .notifyOthersOnDeactivation) } catch { print("会话停用失败:\(error.localizedDescription)") } } } }
内容的提问来源于stack exchange,提问作者Rhythmus
相关产品推荐
相关产品推荐

