From 41eba57f538bc2008080d042cfd440317fb6f09c Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 15 Oct 2025 17:35:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_service/AgentServiceImpl.swift | 88 ++++++++++++------- .../Sources/azure_speech/AzureAsrHelper.swift | 1 + .../ble_service/SwiftBleServicePlugin.swift | 2 +- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index 01c754fb2..e9a756ebf 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -84,6 +84,7 @@ class AgentServiceImpl: NSObject { private var serverurl = "" private var usertoken = "" + private let timerQueue = DispatchQueue(label: "com.example.idleTimer.queue") private var idleTimer: DispatchSourceTimer? private let maxIdleSeconds: TimeInterval = 10 var micCapture: MicrophoneCapture! @@ -360,41 +361,62 @@ class AgentServiceImpl: NSObject { } private func startIdleCheck(idleSeconds: TimeInterval = 10) { - stopIdleCheck() - - guard isRecognizing else { return } - - os_log("启动空闲检测,超时时间: %.0f秒,模式: %{public}@", log: logger, type: .info, idleSeconds, currentRecognitionMode) - - // 使用全局队列而不是主队列,避免后台挂起问题 - let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) - idleTimer = timer - - timer.schedule(deadline: .now() + idleSeconds) - timer.setEventHandler { [weak self] in - // 使用更安全的方式检查self是否存在 - DispatchQueue.main.async { [weak self] in + // 2. 所有对idleTimer的操作都通过串行队列执行,避免竞态条件 + timerQueue.async { + // 先停止旧的定时器(在串行队列中执行,确保线程安全) + self.stopIdleCheck() + + guard self.isRecognizing else { return } + + os_log("启动空闲检测,超时时间: %.0f秒,模式: %{public}@", + log: self.logger, + type: .info, + idleSeconds, + self.currentRecognitionMode) + + // 使用全局队列作为定时器回调队列(避免后台挂起) + let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) + + timer.schedule(deadline: .now() + idleSeconds) + timer.setEventHandler { [weak self] in guard let self = self else { return } - - if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming { - os_log("空闲检测触发自动停止,模式: %{public}@", log: self.logger, type: .info, self.currentRecognitionMode) - // 关闭编码器 (设备侧) - BleService.shared.closeCodec() - self.stopRecognition() - self.sendEvent(name: "auto_stop", data: [ - "reason": "idle_timeout", - "seconds": idleSeconds, - "mode": self.currentRecognitionMode - ]) + + // 回调在全局队列,涉及UI或实例变量操作时切换到主线程 + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + // 检查状态(确保在主线程访问这些属性,避免UI相关竞态) + if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming { + os_log("空闲检测触发自动停止,模式: %{public}@", + log: self.logger, + type: .info, + self.currentRecognitionMode) + + // 关闭编码器(设备侧操作,若BleService线程安全可直接调用,否则需加锁) + BleService.shared.closeCodec() + + self.stopRecognition() + self.sendEvent(name: "auto_stop", data: [ + "reason": "idle_timeout", + "seconds": idleSeconds, + "mode": self.currentRecognitionMode + ]) + } } } + + timer.resume() + // 3. 在串行队列中安全赋值 + self.idleTimer = timer } - timer.resume() } private func stopIdleCheck() { - idleTimer?.cancel() - idleTimer = nil + timerQueue.async { + self.idleTimer?.cancel() + self.idleTimer = nil // 清空引用 + } + } private func restartIdleCheck() { @@ -523,11 +545,11 @@ class AgentServiceImpl: NSObject { if !isRecognizing || !isInitialized { return false } - guard let audioStream = azureAsrHelper?.audioStream else { - os_log("AI音频流未初始化", log: logger,type: .error) - return false -} -audioStream.saveAudioDataTo(data: audioData) + guard let audioStream = azureAsrHelper?.audioStream else { + os_log("AI音频流未初始化", log: logger,type: .error) + return false + } + audioStream.saveAudioDataTo(data: audioData) return true } diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift index ca31912cf..8fe11e856 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift @@ -801,6 +801,7 @@ public class AzureAsrHelper: NSObject { // 在主线程回调结果 DispatchQueue.main.async { callback.onResult(sessionid: self.currsessionId,result.text ?? "", detectedLanguage) + self.currsessionId = UUID().uuidString } } diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift index 7368fe88c..5b0b26fb2 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift @@ -141,7 +141,7 @@ public class SwiftBleServicePlugin: NSObject, FlutterPlugin { case "getPairedMacAddress": let pairedUUID = BleService.shared.getPairedMacAddress() - result(pairedUUID) + result(true) case "clearAssociations": BleService.shared.clearAssociations() result(true)