|
|
|
@ -475,26 +475,57 @@ class IntegratedSpeechTranslationService( |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止连续语音翻译 |
|
|
|
* 停止连续语音翻译(非阻塞) |
|
|
|
* 在 IO 线程内顺序停止识别器、录音线程与文件,并对翻译/合成收尾增加统一超时保护,避免阻塞主线程与潜在 ANR。 |
|
|
|
*/ |
|
|
|
fun stopContinuousTranslation() { |
|
|
|
try { |
|
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
|
audioProcessor?.stopRecording() |
|
|
|
recordfile1!!.closeFile(true) |
|
|
|
Log.d(TAG, "停止连续语音翻译-开始") |
|
|
|
// 先标记识别关闭,避免后续流程继续入队处理 |
|
|
|
serviceState.isRecognizing.set(false) |
|
|
|
// 等待当前处理完成 |
|
|
|
while (serviceState.isTranslating.get() || serviceState.isSynthesizing.get()) { |
|
|
|
Thread.sleep(100) |
|
|
|
} |
|
|
|
|
|
|
|
Log.d(TAG, "停止连续语音翻译") |
|
|
|
// 在 IO 线程中做真实的停止动作,避免阻塞主线程 |
|
|
|
launch(Dispatchers.IO) { |
|
|
|
// 1) 停止识别器:等待短超时,避免卡住 |
|
|
|
try { |
|
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
|
?.get(1500, java.util.concurrent.TimeUnit.MILLISECONDS) |
|
|
|
} catch (t: Throwable) { |
|
|
|
Log.w(TAG, "停止识别器超时或失败", t) |
|
|
|
} |
|
|
|
|
|
|
|
// 2) 停止录音线程(打断并短等待退出) |
|
|
|
runCatching { audioProcessor?.stopRecording() } |
|
|
|
.onFailure { Log.w(TAG, "停止录音失败", it) } |
|
|
|
|
|
|
|
// 3) 关闭录音文件(若开启过录音) |
|
|
|
runCatching { recordfile1?.closeFile(true) } |
|
|
|
.onFailure { Log.w(TAG, "关闭录音文件失败", it) } |
|
|
|
|
|
|
|
// 4) 等待翻译/合成收尾,整体不超过 2s |
|
|
|
val start = System.currentTimeMillis() |
|
|
|
while ((serviceState.isTranslating.get() || serviceState.isSynthesizing.get()) |
|
|
|
&& System.currentTimeMillis() - start < 2000 |
|
|
|
) { |
|
|
|
delay(100) |
|
|
|
} |
|
|
|
if (serviceState.isTranslating.get() || serviceState.isSynthesizing.get()) { |
|
|
|
Log.w(TAG, "等待翻译/合成完成超时,强制标记结束") |
|
|
|
} |
|
|
|
|
|
|
|
// 5) 统一落幕:确保状态关闭 |
|
|
|
serviceState.isTranslating.set(false) |
|
|
|
serviceState.isSynthesizing.set(false) |
|
|
|
|
|
|
|
Log.d(TAG, "停止连续语音翻译-完成") |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "停止翻译失败", e) |
|
|
|
eventCallback?.onError("Service", "停止失败: ${e.message}") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 处理翻译和合成流程 |
|
|
|
*/ |
|
|
|
@ -1184,58 +1215,57 @@ fun getSupportedLanguages(): List<String> { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 改进的资源释放方法 |
|
|
|
* 安全释放音频处理器资源 |
|
|
|
* 添加更强的异常处理和超时保护 |
|
|
|
*/ |
|
|
|
fun dispose() { |
|
|
|
try { |
|
|
|
Log.d(TAG, "开始释放音频处理器资源...") |
|
|
|
|
|
|
|
// 1. 停止运行标志 |
|
|
|
isRunning.set(false) |
|
|
|
// 1. 设置停止标志 |
|
|
|
if (!isRunning.compareAndSet(true, false)) { |
|
|
|
Log.d(TAG, "音频处理器已停止,跳过释放") |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 中断处理线程 |
|
|
|
processingThread?.let { thread -> |
|
|
|
if (thread.isAlive) { |
|
|
|
// 1) 安全停止处理线程 |
|
|
|
processingThread?.let { thread -> |
|
|
|
if (thread.isAlive) { |
|
|
|
try { |
|
|
|
thread.interrupt() |
|
|
|
try { |
|
|
|
// 等待线程结束,最多等待2秒 |
|
|
|
thread.join(2000) |
|
|
|
if (thread.isAlive) { |
|
|
|
Log.w(TAG, "音频处理线程未能正常结束") |
|
|
|
} else { |
|
|
|
Log.d(TAG, "音频处理线程已结束") |
|
|
|
} |
|
|
|
} catch (e: InterruptedException) { |
|
|
|
Log.w(TAG, "等待音频处理线程结束时被中断") |
|
|
|
Thread.currentThread().interrupt() |
|
|
|
thread.join(500) |
|
|
|
if (thread.isAlive) { |
|
|
|
Log.w(TAG, "音频处理线程未在500ms内结束") |
|
|
|
// 不强制杀死,交由系统回收,但继续清理资源 |
|
|
|
} else { |
|
|
|
Log.d(TAG, "音频处理线程已正常结束") |
|
|
|
} |
|
|
|
} catch (e: InterruptedException) { |
|
|
|
Log.w(TAG, "等待线程结束时被中断", e) |
|
|
|
Thread.currentThread().interrupt() |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "停止处理线程时发生异常", e) |
|
|
|
} |
|
|
|
} |
|
|
|
processingThread = null |
|
|
|
|
|
|
|
// 3. 关闭音频流 |
|
|
|
try { |
|
|
|
pushAudioStream?.close() |
|
|
|
pushAudioStream = null |
|
|
|
Log.d(TAG, "音频流已关闭") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "关闭音频流失败", e) |
|
|
|
} |
|
|
|
|
|
|
|
// 4. 清理音频队列 |
|
|
|
try { |
|
|
|
val remainingData = audioQueue.size |
|
|
|
audioQueue.clear() |
|
|
|
Log.d(TAG, "音频队列已清理,丢弃 $remainingData 个数据包") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "清理音频队列失败", e) |
|
|
|
} |
|
|
|
|
|
|
|
Log.d(TAG, "音频处理器资源释放完成") |
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "释放音频处理器资源时发生异常", e) |
|
|
|
} |
|
|
|
processingThread = null |
|
|
|
|
|
|
|
// 2) 关闭输入流 |
|
|
|
try { |
|
|
|
pushAudioStream?.close() |
|
|
|
} catch (t: Throwable) { |
|
|
|
Log.w(TAG, "关闭 PushAudioInputStream 时异常", t) |
|
|
|
} finally { |
|
|
|
pushAudioStream = null |
|
|
|
} |
|
|
|
|
|
|
|
// 3) 清空队列 |
|
|
|
audioQueue.clear() |
|
|
|
|
|
|
|
Log.d(TAG, "音频处理器资源释放完成") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "释放音频处理器资源失败", e) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|