Android中Google Cloud TTS无法同步执行问题的解决方法
问题原因
你遇到的问题是因为onSpeechComplete回调触发时机错误:当前AsyncTask的onPostExecute方法在音频文件准备好、开始播放后就立即执行了,而非等到音频播放完成后才触发,所以日志会提前打印。同时当前的busy标记逻辑无法支持顺序执行多个语音任务,导致并发执行。
解决方案
我们需要调整回调触发时机,并添加任务队列来实现顺序播放:
1. 定义任务队列与内部任务类
在SpeechService中添加队列结构,用于存储待播放的语音任务(包含文本和回调):
// 新增:存储待播放的语音任务 private static class SpeechTask { String text; SpeechCallback callback; SpeechTask(String text, SpeechCallback callback) { this.text = text; this.callback = callback; } } private Queue<SpeechTask> speechQueue;
2. 初始化队列
在SpeechService的构造函数中初始化队列:
private SpeechService(Context context) { this.context = context; // 初始化任务队列 speechQueue = new LinkedList<>(); }
3. 修改speak方法
如果当前正处于播放状态,就把任务加入队列;否则直接执行任务:
public void speak(String text, SpeechCallback callback) { SpeechTask task = new SpeechTask(text, callback); if (busy) { Log.d(TAG, "Speech Service busy! Adding task to queue."); speechQueue.add(task); return; } busy = true; executeSpeechTask(task); } // 新增:执行单个语音任务 private void executeSpeechTask(SpeechTask task) { speechCallback = task.callback; new TextToSpeechTask().execute(task.text); }
4. 调整回调触发时机
将onSpeechComplete的触发从AsyncTask的onPostExecute移到MediaPlayer的onCompletionListener中,同时处理队列的下一个任务:
private void playAudio(byte[] audioData) { try { releaseMediaPlayer(); File tempFile = File.createTempFile("tempAudio", ".pcm", context.getCacheDir()); FileOutputStream fos = new FileOutputStream(tempFile); fos.write(audioData); fos.close(); mediaPlayer = new MediaPlayer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build()); } else { mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } mediaPlayer.setDataSource(tempFile.getAbsolutePath()); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { releaseMediaPlayer(); tempFile.delete(); // 播放完成后触发回调 if (speechCallback != null) { speechCallback.onSpeechComplete(); } // 处理队列中的下一个任务 busy = false; if (!speechQueue.isEmpty()) { SpeechTask nextTask = speechQueue.poll(); executeSpeechTask(nextTask); } } }); mediaPlayer.start(); } catch (IOException e) { Log.e(TAG, "Error playing audio: " + e.getMessage()); // 播放出错时也要重置状态,处理队列任务 busy = false; if (!speechQueue.isEmpty()) { SpeechTask nextTask = speechQueue.poll(); executeSpeechTask(nextTask); } } }
5. 修改AsyncTask的onPostExecute
移除原来的回调触发和busy状态修改,因为现在这些逻辑都在MediaPlayer的完成监听中:
@Override protected void onPostExecute(String result) { // 这里不再处理回调和busy状态,交给MediaPlayer的onCompletion处理 }
效果验证
修改后,当你调用多次speak方法时,语音会按顺序播放,每个语音播放完成后才会触发对应的onSpeechComplete回调,同时日志Done with TTS会在音频播放完成后才打印。
内容的提问来源于stack exchange,提问作者JSNerd01
相关产品推荐
相关产品推荐

