应用终止前获取AVAudioRecorder录音URL失败的问题咨询
解决滑动关闭APP时录音代理未触发的问题
我太懂你这个困扰了——明明在applicationWillTerminate里调用了stop(),但audioRecorderDidFinishRecording和错误代理就是死活不执行,连录音URL都拿不到。之前做语音笔记APP时我也踩过这个坑,核心问题其实和系统的异步回调机制有关,咱们来一步步解决:
问题根源
AVAudioRecorder的stop()方法是异步触发代理回调的,而applicationWillTerminate的5秒窗口期看似充裕,但系统可能在代理回调执行前就直接终止了APP进程,导致代理代码根本没机会运行。
解决方案:放弃等待代理,主动控制录音收尾
与其依赖不可靠的代理回调,不如提前规划录音文件路径,在APP终止时直接处理预存的文件路径,同时用后台任务争取更多执行时间。
修改后的代码示例
import AVFoundation import UIKit class RecorderManager: NSObject, AVAudioRecorderDelegate { var micRecorder: AVAudioRecorder? var recordingURL: URL? // 提前保存录音文件的URL private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid override init() { super.init() setupNotificationObserver() } private func setupNotificationObserver() { NotificationCenter.default.addObserver( self, selector: #selector(applicationWillTerminate(notification:)), name: UIApplication.willTerminateNotification, object: nil ) } // 启动录音时就确定并保存文件URL func startRecording() { guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { print("无法获取文档目录") return } // 生成唯一的录音文件名,避免覆盖 let timestamp = Date().timeIntervalSince1970 recordingURL = documentsDir.appendingPathComponent("recording_\(timestamp).m4a") let audioSettings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100.0, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] do { micRecorder = try AVAudioRecorder(url: recordingURL!, settings: audioSettings) micRecorder?.delegate = self micRecorder?.prepareToRecord() micRecorder?.record() } catch { print("录音初始化失败:\(error.localizedDescription)") recordingURL = nil } } @objc private func applicationWillTerminate(notification: Notification) { guard let recorder = micRecorder, recorder.isRecording else { return } // 启动后台任务,告诉系统"我还有收尾工作没做完" backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "FinishRecording") { [weak self] in // 超时后强制结束后台任务 UIApplication.shared.endBackgroundTask(self?.backgroundTaskID ?? .invalid) self?.backgroundTaskID = .invalid } // 停止录音 recorder.stop() // 不等代理,直接用预存的URL处理文件 if let url = recordingURL, FileManager.default.fileExists(atPath: url.path) { saveRecordingToCoreData(url: url) print("APP终止时成功保存录音:\(url)") } else { print("录音文件不存在,保存失败") } // 手动结束后台任务 if backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } } // 正常录音结束时的代理回调(依然保留,处理非终止场景) func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if flag { saveRecordingToCoreData(url: recorder.url) } else { print("正常录音结束失败") } } func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { if let error = error { print("录音编码错误:\(error.localizedDescription)") } } // 封装CoreData保存逻辑 private func saveRecordingToCoreData(url: URL) { // 这里实现你的CoreData保存逻辑,比如将URL路径转成字符串存储 // 示例: // let recordingEntity = RecordingEntity(context: persistentContainer.viewContext) // recordingEntity.filePath = url.path // try? persistentContainer.viewContext.save() } }
关键优化点
- 提前保存录音URL:在启动录音时就生成并存储文件路径,避免依赖代理返回的URL
- 使用后台任务:通过
beginBackgroundTask向系统申请额外的执行时间,确保文件保存完成 - 主动处理文件:APP终止时直接用预存URL操作文件,不再等待异步代理回调
- 保留原有代理:正常场景下(用户主动停止录音),代理依然可以正常工作
内容的提问来源于stack exchange,提问作者Lance Samaria
相关产品推荐
相关产品推荐

