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

Kotlin中解决SpeechRecognizer需在主线程运行的报错问题

解决SpeechRecognizer必须在主线程运行的问题

问题根源在于UtteranceProgressListener的回调方法(比如onDone)是在后台线程执行的,而Android要求SpeechRecognizer的创建和调用必须在应用主线程,直接调用就会触发报错。

修改方案

只需要把startSTT()的调用切换到主线程即可,有两种简单实现方式:

方式1:使用Service的runOnUiThread方法

因为NotificationListenerService是Service的子类,自带runOnUiThread方法,可以直接用来切换到主线程:

修改UtteranceProgressListener的onDone方法:

tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
    override fun onDone(utteranceId: String) {
        // 切换到主线程执行startSTT
        runOnUiThread {
            startSTT()
        }
    }

    override fun onError(utteranceId: String) {}
    override fun onStart(utteranceId: String) {}
})

方式2:使用主线程Looper创建Handler

如果需要更灵活的线程切换,可以提前初始化一个绑定主线程的Handler:

  1. 在Service类中添加Handler实例:
class NotificationListener : NotificationListenerService() {
    // 绑定主线程Looper的Handler
    private val mainHandler = Handler(Looper.getMainLooper())

    // 其他代码...
}
  1. 在onDone中通过Handler.post切换线程:
override fun onDone(utteranceId: String) {
    mainHandler.post {
        startSTT()
    }
}

修改后的完整代码示例

class NotificationListener : NotificationListenerService() {
    private val mainHandler = Handler(Looper.getMainLooper())
    private var tts: TextToSpeech? = null

    private fun startSTT() {
        val speechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
            putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, packageName)
            putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
        }
        val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this)
        speechRecognizer.setRecognitionListener(recognitionListener())
        speechRecognizer.startListening(speechRecognizerIntent)
    }

    private fun initTextToSpeech() {
        tts = TextToSpeech(this) {
            if (it == TextToSpeech.SUCCESS) {
                val result = tts?.setLanguage(Locale.KOREA)
                if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
                    Toast.makeText(this, "language not supported", Toast.LENGTH_SHORT).show()
                    return@TextToSpeech
                }
                tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
                    override fun onDone(utteranceId: String) {
                        // 用Handler切换到主线程
                        mainHandler.post {
                            startSTT()
                        }
                    }

                    override fun onError(utteranceId: String) {}
                    override fun onStart(utteranceId: String) {}
                })
            }
        }
    }

    // 补充实现recognitionListener方法(原代码中未给出)
    private fun recognitionListener(): RecognitionListener {
        return object : RecognitionListener {
            override fun onReadyForSpeech(params: Bundle?) {}
            override fun onBeginningOfSpeech() {}
            override fun onRmsChanged(rmsdB: Float) {}
            override fun onBufferReceived(buffer: ByteArray?) {}
            override fun onEndOfSpeech() {}
            override fun onError(error: Int) {}
            override fun onResults(results: Bundle?) {
                // 处理识别结果
                results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)?.let {
                    val recognizedText = it.firstOrNull()
                    // 这里可以把识别到的文本发送为消息
                }
            }
            override fun onPartialResults(partialResults: Bundle?) {}
            override fun onEvent(eventType: Int, params: Bundle?) {}
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:25:21