自定义Android TextToSpeechService无法接收Google TalkBack使用提示
问题分析与解决方案
核心错误原因
你的自定义TextToSpeechService违反了系统TTS服务的核心规范:
- 系统TTS服务的职责是生成PCM音频数据并通过
SynthesisCallback返回给调用方(比如TalkBack),而非直接调用另一个TTS引擎的speak()方法播放音频。 - 直接调用Google TTS的
speak()会绕开系统的音频调度机制,和TalkBack的音频输出抢占通道,导致TalkBack的提示音被覆盖或无法触发;同时你在调用speak()后立刻执行callback.done(),系统会认为合成已完成,但Google TTS的异步播放还在进行,阻塞后续的TalkBack请求。
修正方案
需要让Google TTS生成音频数据,再通过自定义服务的SynthesisCallback将数据返回给系统,由系统统一调度播放。以下是修改后的关键代码:
1. 单例类中的合成逻辑修改
private TextToSpeech mAndroidTTS = null; private SynthesisCallback mCurrentCallback; private String mTempAudioPath; public void initEnglishTTS(Context context) { if (mAndroidTTS == null) { mAndroidTTS = new TextToSpeech(context, status -> { if (status == TextToSpeech.SUCCESS) { mAndroidTTS.setLanguage(Locale.US); // 设置 utterance 监听,用于捕获合成完成事件 mAndroidTTS.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) {} @Override public void onDone(String utteranceId) { if (mCurrentCallback == null) return; try { // 读取临时音频文件,通过回调输出给系统 FileInputStream fis = new FileInputStream(mTempAudioPath); byte[] buffer = new byte[4096]; int bytesRead; mCurrentCallback.start(16000, AudioFormat.ENCODING_PCM_16BIT, 1); while ((bytesRead = fis.read(buffer)) != -1) { mCurrentCallback.audioAvailable(buffer, 0, bytesRead); } fis.close(); // 清理临时文件 new File(mTempAudioPath).delete(); mCurrentCallback.done(); } catch (IOException e) { mCurrentCallback.error(TextToSpeech.ERROR_IO); } finally { mCurrentCallback = null; } } @Override public void onError(String utteranceId) { if (mCurrentCallback != null) { mCurrentCallback.error(TextToSpeech.ERROR); new File(mTempAudioPath).delete(); mCurrentCallback = null; } } }); } }, "com.google.android.tts"); } } public void synthesize(String text, SynthesisCallback callback) { if (mAndroidTTS == null || mAndroidTTS.getEngines().isEmpty()) { callback.error(TextToSpeech.ERROR); return; } mCurrentCallback = callback; // 创建内部缓存目录的临时文件,避免权限问题 mTempAudioPath = mContext.getCacheDir().getAbsolutePath() + "/tts_temp_" + System.currentTimeMillis() + ".pcm"; HashMap<String, String> params = new HashMap<>(); String utteranceId = UUID.randomUUID().toString(); params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId); // 调用Google TTS合成音频到临时文件 int result = mAndroidTTS.synthesizeToFile(text, params, mTempAudioPath); if (result != TextToSpeech.SUCCESS) { callback.error(result); mCurrentCallback = null; } } // 移除原有的sayInEnglish方法
2. 自定义TTS服务代码保持不变
public class CustomTtsService extends TextToSpeechService { private SynthesizerSingleton singleton = SynthesizerSingleton.getInstance(); @Override protected void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback) { String text = getRequestString(request); singleton.synthesize(text, callback); } private String getRequestString(SynthesisRequest request) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return request.getCharSequenceText().toString(); } else { return request.getText(); } } }
额外注意事项
- 无需额外权限:使用应用内部缓存目录存储临时音频文件,不需要申请
WRITE_EXTERNAL_STORAGE权限。 - 异常处理:务必处理合成失败、文件读取失败等情况,避免内存泄漏或回调未关闭。
- 替代方案:如果不需要自定义非英文文本的合成逻辑,可直接通过系统TTS引擎的语言匹配机制,让系统自动为英文选择Google TTS引擎,无需在自定义服务中嵌套调用。
内容的提问来源于stack exchange,提问作者arkate
相关产品推荐
相关产品推荐

