圆山中庸 2025-10-27 17:45 采纳率: 98.5%
浏览 0
已采纳

AVAudioRecorder录音中断后无法恢复续录

使用AVAudioRecorder进行录音时,若因来电、锁屏或被其他音频会话抢占而中断,录音会话将进入非活跃状态。此时调用`record`方法尝试恢复录音,往往无法继续写入原文件,导致续录失败。核心问题在于中断后未正确处理`AVAudioSession`的重新激活及资源重建。即使监听了中断通知并尝试重启录制,AVAudioRecorder实例可能已处于不可恢复状态,需重新初始化实例并追加录音数据。常见错误是直接调用`pause`或`stop`后再`record`,忽略了中断后必须重新配置音频会话和创建新录制器。
  • 写回答

1条回答 默认 最新

  • 桃子胖 2025-10-27 18:01
    关注

    1. 问题背景与常见现象

    在iOS开发中,使用AVAudioRecorder进行音频录制时,开发者常遇到一个棘手的问题:当录音过程中发生来电、锁屏或被其他应用(如Safari播放视频、微信语音通知)抢占音频会话时,当前的AVAudioSession会被系统中断并进入非活跃状态。此时若尝试通过调用record()方法恢复录音,往往发现无法继续写入原始文件,导致续录失败。

    该现象的根本原因在于,中断事件发生后,AVAudioRecorder内部持有的资源和状态可能已损坏或失效,即使音频会话后续恢复,原实例也无法重新绑定到有效的音频流。许多开发者误以为只需监听中断通知(AVAudioSession.interruptionNotification),并在中断结束时调用pausestop后再record即可恢复,实则忽略了必须重新激活AVAudioSession并重建AVAudioRecorder实例的关键步骤。

    2. 技术栈层级分析

    为深入理解该问题,需从以下技术层级逐步剖析:

    • AVAudioSession:负责管理应用的音频行为,包括类别设置(如.playAndRecord)、模式、选项及与其他音频应用的协调。
    • AVAudioRecorder:基于AVAudioSession运行,封装了录音流程,但其生命周期依赖于会话的活跃性。
    • 中断机制:系统通过NSNotificationCenter发送中断开始与结束的通知,其中包含中断类型(暂停/停止)和是否可恢复等信息。
    • 文件追加逻辑:若需实现“断点续录”,不能依赖原AVAudioRecorder继续写入同一文件,而应创建新实例并将数据合并至目标文件。

    3. 中断处理流程图解

    graph TD
        A[开始录音] --> B{是否发生中断?}
        B -- 是 --> C[收到AVAudioSession.interruptionNotification]
        C --> D[检查中断类型: .began]
        D --> E[暂停UI, 标记状态为中断]
        B -- 否 --> F[正常录音]
        G[中断结束通知] --> H{中断是否可恢复?}
        H -- 可恢复 --> I[重新激活AVAudioSession]
        I --> J[重新配置AVAudioRecorder]
        J --> K[创建新实例并追加录音]
        H -- 不可恢复 --> L[放弃续录或提示用户]
    

    4. 常见错误实践示例

    错误做法后果正确替代方案
    仅监听中断通知后调用record()无实际录音,静音或崩溃重新初始化AVAudioRecorder
    使用pause保存状态中断后无法恢复写入中断即视为暂停,需重建实例
    未处理AVAudioSession激活失败后续录音失败异步重试激活,带超时机制
    直接复用原文件路径启动新录制覆盖原内容或格式错误使用append模式或后期合并

    5. 正确解决方案实现

    以下是处理中断并实现续录的核心代码结构:

    
    class AudioRecorderManager {
        var recorder: AVAudioRecorder?
        var isInterrupted = false
        let settings: [String: Any] = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 1
        ]
    
        func setupRecorder() throws {
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setCategory(.playAndRecord, mode: .default)
            try audioSession.setActive(true)
            
            let url = getRecordingURL() // 自定义路径
            recorder = try AVAudioRecorder(url: url, settings: settings)
            recorder?.isMeteringEnabled = true
        }
    
        @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:
                isInterrupted = true
                recorder?.pause() // 仅UI反馈
            case .ended:
                isInterrupted = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.resumeRecording()
                }
            @unknown default: break
            }
        }
    
        private func resumeRecording() {
            do {
                try AVAudioSession.sharedInstance().setActive(true)
                try setupRecorder() // 重建实例
                recorder?.record()
            } catch {
                print("Failed to resume recording: $error)")
            }
        }
    }
    

    6. 进阶建议与最佳实践

    1. 使用CombineRxSwift统一管理音频状态流,避免状态散落。
    2. 对长时间录音场景,考虑分段录制后合并,提升容错能力。
    3. 在后台模式下启用UIBackgroundTaskIdentifier延长执行时间。
    4. 监控AVAudioSession.routeChangeNotification防止耳机拔出导致中断。
    5. 使用os_log记录关键状态变化,便于线上问题排查。
    6. 测试覆盖多种中断组合:来电+FaceTime+音乐播放+低电量自动锁屏。
    7. 对于高保真需求,可切换至AudioQueueAVAudioEngine以获得更细粒度控制。
    8. 确保Info.plist中声明UIBackgroundModes支持音频后台运行。
    9. 在重建AVAudioRecorder前验证文件路径可写性和磁盘空间。
    10. 提供用户界面反馈中断状态,增强体验一致性。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

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