You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 常见问题排查

如果你的监听器还是不触发,检查这几点:

  1. 确认TTS初始化是否成功(看Log里的TTS日志)
  2. 确认调用speak时是否传入了正确的utterance ID参数
  3. 确认设备的TTS引擎是否正常(可以测试系统自带的语音播报功能)

内容的提问来源于stack exchange,提问作者Nelson Wright

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:56:38