穆晶波 2025-11-10 04:50 采纳率: 98.7%
浏览 1
已采纳

iOS录音因来电或闹钟中断如何处理?

在iOS应用开发中,使用AVAudioRecorder进行录音时,常遇到因来电或闹钟触发导致录音被意外中断的问题。即使应用进入后台,系统会暂停音频会话,造成录音停止且无法自动恢复。开发者需监听AVAudioSession.interruptionNotification通知,正确处理中断开始与结束状态。但常见问题在于:中断结束后,部分设备未能正确重新激活音频会话,或未及时重启录音,导致录音丢失。如何确保在中断恢复后无缝继续录音,并兼容不同iOS版本的音频会话行为,成为稳定实现后台录音的关键挑战。
  • 写回答

2条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-11-10 12:46
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案能帮助您,请采纳,非常感谢!
    .
    每日一句正能量,让它带给您好心情:努力的人,自带光芒,走到哪里都能照亮一片天。
    .

    在 iOS 开发中处理录音中断需要正确处理音频会话中断通知,并在中断结束后重新激活会话和录音。以下是完整的实现方案:

    1. 设置音频会话类别

    import AVFoundation
    
    
    func setupAudioSession() {
        do {
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetooth])
            try audioSession.setActive(true)
        } catch {
            print("设置音频会话失败: \(error.localizedDescription)")
        }
    }
    

    2. 注册中断通知

    class AudioRecorderManager: NSObject {
        private var audioRecorder: AVAudioRecorder?
        private var isInterrupted = false
        private var shouldResume = false
        
        override init() {
            super.init()
            setupNotifications()
        }
        
        private func setupNotifications() {
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(handleAudioSessionInterruption(_:)),
                name: AVAudioSession.interruptionNotification,
                object: nil
            )
            
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(handleAppWillResignActive),
                name: UIApplication.willResignActiveNotification,
                object: nil
            )
            
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(handleAppDidBecomeActive),
                name: UIApplication.didBecomeActiveNotification,
                object: nil
            )
        }
    }
    

    3. 处理音频中断的核心逻辑

    extension AudioRecorderManager {
        @objc private func handleAudioSessionInterruption(_ notification: Notification) {
            guard let userInfo = notification.userInfo,
                  let interruptionTypeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
                  let interruptionType = AVAudioSession.InterruptionType(rawValue: interruptionTypeValue) else {
                return
            }
            
            switch interruptionType {
            case .began:
                // 中断开始
                handleInterruptionBegan()
            case .ended:
                // 中断结束
                guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
                let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
                handleInterruptionEnded(shouldResume: options.contains(.shouldResume))
            @unknown default:
                break
            }
        }
        
        private func handleInterruptionBegan() {
            isInterrupted = true
            
            // 暂停录音但不释放资源
            audioRecorder?.pause()
            
            // 保存当前录音状态
            shouldResume = audioRecorder?.isRecording ?? false
            
            print("录音被中断")
        }
        
        private func handleInterruptionEnded(shouldResume: Bool) {
            isInterrupted = false
            
            // 重新激活音频会话
            do {
                let audioSession = AVAudioSession.sharedInstance()
                try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
                
                // 如果需要恢复且之前正在录音
                if shouldResume && self.shouldResume {
                    resumeRecording()
                }
                
            } catch {
                print("重新激活音频会话失败: \(error.localizedDescription)")
                // 可以在这里添加重试逻辑
                attemptToRecoverFromError()
            }
        }
    }
    

    4. 恢复录音的实现

    extension AudioRecorderManager {
        private func resumeRecording() {
            guard let recorder = audioRecorder else { return }
            
            // 检查录音器状态
            if recorder.isRecording {
                return // 已经在录音
            }
            
            do {
                // iOS 16+ 兼容性处理
                if #available(iOS 16.0, *) {
                    // 确保音频会话已激活
                    let audioSession = AVAudioSession.sharedInstance()
                    if !audioSession.isOtherAudioPlaying {
                        try audioSession.setActive(true)
                    }
                }
                
                // 恢复录音
                recorder.record()
                print("录音已恢复")
                
            } catch {
                print("恢复录音失败: \(error.localizedDescription)")
                // 失败后尝试重新创建录音器
                recreateRecorderAfterInterruption()
            }
        }
        
        private func recreateRecorderAfterInterruption() {
            guard let currentRecorder = audioRecorder,
                  let url = currentRecorder.url else { return }
            
            let settings = currentRecorder.settings
            let currentTime = currentRecorder.currentTime
            
            do {
                // 创建新的录音器实例
                let newRecorder = try AVAudioRecorder(url: url, settings: settings)
                newRecorder.delegate = self
                newRecorder.isMeteringEnabled = true
                
                if newRecorder.prepareToRecord() {
                    audioRecorder = newRecorder
                    
                    // 从上次中断的位置继续录音
                    if newRecorder.record(atTime: currentTime) {
                        print("录音器重新创建并恢复成功")
                    }
                }
            } catch {
                print("重新创建录音器失败: \(error.localizedDescription)")
            }
        }
    }
    

    5. 应用前后台状态处理

    extension AudioRecorderManager {
        @objc private func handleAppWillResignActive() {
            // 应用进入后台前的处理
            if audioRecorder?.isRecording == true {
                shouldResume = true
            }
        }
        
        @objc private func handleAppDidBecomeActive() {
            // 应用回到前台后的检查
            if shouldResume && !isInterrupted {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.resumeRecording()
                }
            }
        }
        
        private func attemptToRecoverFromError() {
            // 错误恢复策略
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                do {
                    try AVAudioSession.sharedInstance().setActive(true)
                    self.resumeRecording()
                } catch {
                    print("恢复尝试失败: \(error.localizedDescription)")
                }
            }
        }
    }
    

    6. 完整的录音管理器示例

    class AudioRecorderManager: NSObject, AVAudioRecorderDelegate {
        static let shared = AudioRecorderManager()
        
        private var audioRecorder: AVAudioRecorder?
        private var isInterrupted = false
        private var shouldResume = false
        private var currentRecordingURL: URL?
        
        private override init() {
            super.init()
            setupNotifications()
            setupAudioSession()
        }
        
        func startRecording() throws {
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setActive(true)
            
            let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let audioFilename = documentsPath.appendingPathComponent("recording-\(Date().timeIntervalSince1970).m4a")
            currentRecordingURL = audioFilename
            
            let settings: [String: Any] = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 44100.0,
                AVNumberOfChannelsKey: 1,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]
            
            audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
            audioRecorder?.delegate = self
            audioRecorder?.isMeteringEnabled = true
            audioRecorder?.prepareToRecord()
            
            if audioRecorder?.record() == true {
                print("开始录音")
            }
        }
        
        func stopRecording() {
            audioRecorder?.stop()
            shouldResume = false
        }
        
        // AVAudioRecorderDelegate
        func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
            print("录音完成: \(flag)")
        }
        
        func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) {
            if let error = error {
                print("录音编码错误: \(error.localizedDescription)")
            }
        }
        
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    }
    

    关键要点

    1. 及时处理中断通知:在中断开始时暂停录音,保存状态
    2. 正确重新激活会话:中断结束后重新激活音频会话
    3. 兼容性处理:考虑不同 iOS 版本的音频会话行为差异
    4. 错误恢复机制:提供重试逻辑来处理恢复失败的情况
    5. 前后台状态协调:结合应用生命周期管理录音状态

    这种实现方案能够有效处理来电、闹钟等系统事件导致的中断,确保录音能够在中断结束后无缝恢复。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 已采纳回答 11月11日
  • 创建了问题 11月10日