|
|
|
@ -386,40 +386,29 @@ class AgentServiceImpl: NSObject { |
|
|
|
} |
|
|
|
|
|
|
|
private func startIdleCheck(idleSeconds: TimeInterval = 10) { |
|
|
|
// 2. 所有对idleTimer的操作都通过串行队列执行,避免竞态条件 |
|
|
|
timerQueue.async { |
|
|
|
// 先停止旧的定时器(在串行队列中执行,确保线程安全) |
|
|
|
self.stopIdleCheck() |
|
|
|
|
|
|
|
if self.idleTimer != nil { |
|
|
|
self.idleTimer?.cancel() |
|
|
|
self.idleTimer = nil |
|
|
|
} |
|
|
|
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 } |
|
|
|
|
|
|
|
// 回调在全局队列,涉及UI或实例变量操作时切换到主线程 |
|
|
|
DispatchQueue.main.async { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
// 检查状态(确保在主线程访问这些属性,避免UI相关竞态) |
|
|
|
os_log("空闲检测触发自动停止,模式: %{public}@", |
|
|
|
log: self.logger, |
|
|
|
type: .info, |
|
|
|
self.currentRecognitionMode) |
|
|
|
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", |
|
|
|
@ -429,9 +418,7 @@ class AgentServiceImpl: NSObject { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
timer.resume() |
|
|
|
// 3. 在串行队列中安全赋值 |
|
|
|
self.idleTimer = timer |
|
|
|
} |
|
|
|
} |
|
|
|
|