Android Studio中如何循环文本语音?TTS合成报错求助
解决TextToSpeech空指针及循环播放问题
错误根源分析
你碰到的java.lang.NullPointerException原因很明确——mtts这个TextToSpeech对象还没完成初始化(处于null状态),就直接调用了它的synthesizeToFile方法。另外还有个隐藏坑:synthesizeToFile是异步操作,你刚调用完就立刻用MediaPlayer去读取文件,这时候文件大概率还没生成,也会导致播放失败。
分步解决方案
1. 正确初始化TextToSpeech实例
你需要在Activity的生命周期(比如onCreate)里完成TextToSpeech的初始化,并且通过OnInitListener确认初始化成功后再使用它:
private TextToSpeech mtts; private MediaPlayer mediaPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); // 初始化MediaPlayer mediaPlayer = new MediaPlayer(); // 初始化TextToSpeech mtts = new TextToSpeech(this, new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { // 设置语言,这里以英语为例 int result = mtts.setLanguage(Locale.US); if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Toast.makeText(MainActivity.this, "不支持当前语言", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "TTS初始化完成", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(MainActivity.this, "TTS初始化失败", Toast.LENGTH_SHORT).show(); } } }); // 设置ImageButton点击事件 imageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 先判断TTS是否准备好 if (mtts == null || mtts.getStatus() != TextToSpeech.SUCCESS) { Toast.makeText(getApplicationContext(), "TTS未准备好,请稍后再试", Toast.LENGTH_SHORT).show(); return; } // 停止当前正在播放的音频(如果有的话) if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.reset(); } HashMap<String, String> myHashRender = new HashMap<>(); String textToConvert = "welcome to the center"; // 推荐使用应用私有目录,无需申请外部存储权限 String destinationFileName = getFilesDir().getAbsolutePath() + "/" + "test.wav"; // 设置Utterance ID,用于监听合成完成事件 String utteranceId = "tts_synthesis_done"; myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId); // 设置合成进度监听,等待合成完成再启动播放 mtts.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) {} @Override public void onDone(String utteranceId) { // 切换回主线程更新UI和启动播放 runOnUiThread(new Runnable() { @Override public void run() { try { mediaPlayer.setDataSource(destinationFileName); mediaPlayer.setLooping(true); mediaPlayer.prepare(); mediaPlayer.start(); v.setBackgroundResource(R.drawable.ic_image_play); Toast.makeText(getApplicationContext(), "开始循环播放", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "播放失败", Toast.LENGTH_SHORT).show(); } } }); } @Override public void onError(String utteranceId) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "合成音频失败", Toast.LENGTH_SHORT).show(); } }); } }); // 发起合成请求 int synthesisResult = mtts.synthesizeToFile(textToConvert, myHashRender, destinationFileName); if (synthesisResult == TextToSpeech.SUCCESS) { Toast.makeText(getApplicationContext(), "正在合成音频", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "合成请求提交失败", Toast.LENGTH_SHORT).show(); } } }); } // 销毁Activity时释放资源,避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); if (mtts != null) { mtts.stop(); mtts.shutdown(); } if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } }
2. 权限优化(可选但推荐)
如果使用上面代码中的应用私有目录(getFilesDir()),不需要在AndroidManifest.xml中添加外部存储权限;如果坚持使用外部存储,需要添加以下权限,并且在Android 6.0+版本中动态申请:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
3. 额外注意事项
- 每次播放前务必重置MediaPlayer,避免资源占用冲突;
- 如果需要支持多语言,可根据需求动态切换
Locale; - 测试时确保设备安装了可用的TTS引擎(比如Google文字转语音)。
内容的提问来源于stack exchange,提问作者Evil Kick
相关产品推荐
相关产品推荐

