Kotlin中配置Android UtteranceProgressListener遇问题求助
解决Kotlin中TextToSpeech的UtteranceProgressListener配置问题
我来帮你搞定这个UtteranceProgressListener的配置问题!很多人第一次用的时候都会卡在最后一步,核心问题通常是忽略了必须设置utterance ID这个关键点,或者监听器的时机不对。
先给你一个完整的可运行示例,然后我再拆解关键点:
1. 完整的TextToSpeech初始化与监听器设置
import android.os.Bundle import android.speech.tts.TextToSpeech import android.speech.tts.UtteranceProgressListener import android.util.Log import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import java.util.Locale class MainActivity : AppCompatActivity() { private var tts: TextToSpeech? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化TextToSpeech tts = TextToSpeech(this) { status -> if (status == TextToSpeech.SUCCESS) { // 设置目标语言(这里以中文简体为例) val languageResult = tts?.setLanguage(Locale.CHINESE) if (languageResult == TextToSpeech.LANG_MISSING_DATA || languageResult == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e("TTS", "当前设备不支持中文播报") } else { // 初始化成功后,配置UtteranceProgressListener setupTTSListener() } } else { Log.e("TTS", "TextToSpeech初始化失败") } } } // 配置UtteranceProgressListener private fun setupTTSListener() { tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() { // 播报开始时触发 override fun onStart(utteranceId: String?) { // 注意:回调在子线程,更新UI必须切回主线程 runOnUiThread { Toast.makeText(this@MainActivity, "语音播报开始", Toast.LENGTH_SHORT).show() // 这里可以添加你需要的开始状态逻辑,比如更新UI、记录状态等 } } // 播报完成时触发 override fun onDone(utteranceId: String?) { runOnUiThread { Toast.makeText(this@MainActivity, "语音播报结束", Toast.LENGTH_SHORT).show() // 处理结束状态,比如重置按钮状态、执行后续任务等 } } // 播报出错时触发(基础版) override fun onError(utteranceId: String?) { runOnUiThread { Toast.makeText(this@MainActivity, "语音播报出错", Toast.LENGTH_SHORT).show() } } // 可选:重写带错误码的onError,获取更详细的错误信息 override fun onError(utteranceId: String?, errorCode: Int) { super.onError(utteranceId, errorCode) runOnUiThread { val errorMsg = when (errorCode) { TextToSpeech.ERROR_INVALID_REQUEST -> "无效请求" TextToSpeech.ERROR_NETWORK -> "网络错误" TextToSpeech.ERROR_NETWORK_TIMEOUT -> "网络超时" TextToSpeech.ERROR_NOT_INSTALLED_YET -> "TTS引擎未安装" TextToSpeech.ERROR_OUTPUT -> "音频输出错误" TextToSpeech.ERROR_SERVICE -> "TTS服务错误" TextToSpeech.ERROR_SYNTHESIS -> "语音合成错误" else -> "未知错误" } Toast.makeText(this@MainActivity, "播报出错:$errorMsg", Toast.LENGTH_SHORT).show() } } }) } // 触发语音播报的方法(调用时必须传入utterance ID) fun startSpeaking(content: String) { val ttsParams = HashMap<String, String>().apply { // 必须设置唯一的utterance ID,否则监听器不会触发回调! put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "tts_task_${System.currentTimeMillis()}") } // QUEUE_FLUSH:立即停止当前播报,开始新的播报;QUEUE_ADD:加入队列等待 tts?.speak(content, TextToSpeech.QUEUE_FLUSH, ttsParams, null) } // 记得在页面销毁时释放TTS资源,避免内存泄漏 override fun onDestroy() { super.onDestroy() tts?.stop() tts?.shutdown() tts = null } }
2. 关键注意点(必看!)
- 必须在TTS初始化成功后设置监听器:一定要在
onInit回调确认status == TextToSpeech.SUCCESS之后再调用setupTTSListener,否则会因为TTS实例未就绪导致监听器设置无效。 - 必须设置utterance ID:调用
speak方法时,必须传入包含TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID的参数Map,这是监听器触发回调的核心依据。你可以用时间戳或者自定义唯一标识来区分不同的播报任务。 - 回调在子线程执行:UtteranceProgressListener的所有回调方法都是在子线程运行的,如果需要更新UI(比如Toast、按钮状态),必须用
runOnUiThread切换到主线程,否则会抛出异常。 - 及时释放资源:在Activity的
onDestroy方法中一定要停止播报并关闭TTS服务,避免内存泄漏。
3. 常见问题排查
如果你的监听器还是不触发,检查这几点:
- 确认TTS初始化是否成功(看Log里的TTS日志)
- 确认调用
speak时是否传入了正确的utterance ID参数 - 确认设备的TTS引擎是否正常(可以测试系统自带的语音播报功能)
内容的提问来源于stack exchange,提问作者Nelson Wright
相关产品推荐
相关产品推荐

