Android TTS 在屏幕关闭一段时间后停止朗读

问题描述 投票:0回答:3

我正在开发一个应用程序,需要在屏幕关闭后继续朗读文本。为了实现这个目标,我将文本转语音(TTS)代码放在前台服务中,这样当屏幕关闭时,TTS 可以继续运行。

以前在我的手机上运行得很好。但是当我将手机从 Android 11 升级到 Android 12 后,TTS 在屏幕关闭一段时间后(通常是几分钟后)停止工作。

通常,TTS 说完一句话后,它会调用

onDone
UtteranceProgressListener
方法,这样我就可以在那里让 TTS 说出下一句话。 TTS 停止工作的原因是,在屏幕关闭一段时间后,
onDone
方法停止被调用。它不会立即停止,而是几分钟后停止,有时更长,有时更短。

编辑:

一开始我关闭了整个系统的电池优化,但是不起作用。然后我关闭单个应用程序的电池优化。我需要转到单个应用程序的设置并将其关闭,或者以编程方式执行此操作,如下所示:

检查应用程序是否启用电池优化

关闭单个应用的电池优化后,这个问题得到了很大改善。然而,TTS 仍然会时不时地停止一次,大约一次持续几个小时。我还注意到,即使电池优化打开,应用程序“T2S”也可以继续运行。我该怎么做才能让 TTS 在电池优化开启时继续运行,就像“T2S”一样,或者至少在电池优化关闭后不让它停止?

android background text-to-speech foreground-service screen-off
3个回答
3
投票

补充 Denny Hsu 的答案:

每个 tts 引擎都有一次合成为语音的最大字符数。您可以通过以下方式找到这个最大数量:

TextToSpeech.getMaxSpeechInputLength()

例如,我认为 Google TTS 引擎的最大长度是 4000 个字符。

您可以通过阻止用户尝试说出超过此限制的文本,或者在此限制处或之前将文本分成单独的字符串,然后按顺序发送来解决此问题。

在后一种情况下,最好找到限制之前的最后一个句子的结尾,并将其分开以确保保持正确的发音。


1
投票

即使关闭电池优化,TTS也会偶尔停止运行,根本原因是

speak()
功能的输入文本太长。

顺便说一下,Android 13 似乎已经解决了这个问题。无需打开电池优化即可让 TTS 在屏幕关闭时继续运行。


0
投票

即使应用程序处于后台,此代码也可以在 Android 12 中运行

class TTS : Service(),  OnInitListener {
  
    private var tts: TextToSpeech? = null
    private lateinit var spokenText: String
    private var isInit: Boolean = false

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if(intent?.extras != null) {
            spokenText = intent.getStringExtra("text").toString()
        }
        else {
            spokenText = ""
        }
        Log.d(TAG, "onStartCommand: $spokenText")
        return START_NOT_STICKY
    }

    override fun onCreate() {
        tts = TextToSpeech(this, this)
        Log.d(TAG, "onCreate: CREATING AGAIN !!")
    }

    override fun onInit(status: Int) {
        if (status == TextToSpeech.SUCCESS) {
            Log.d(TAG, "onInit: TextToSpeech Success")
            val result = tts!!.setLanguage(Locale("hi", "IN"))
            if (result != TextToSpeech.LANG_MISSING_DATA && result != TextToSpeech.LANG_NOT_SUPPORTED) {
                Log.d(TAG, "onInit: speaking........")
                addAudioAttributes()
                isInit = true
            }
        }
        else {
            Log.d(TAG, "onInit: TTS initialization failed")
            Toast.makeText(
                applicationContext,
                "Your device don't support text to speech.\n Visit app to download!!",
                Toast.LENGTH_SHORT
            ).show()
        }
    }

    private fun addAudioAttributes() {
        val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val audioAttributes = AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build()
            tts?.setAudioAttributes(audioAttributes)
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val focusRequest =
                AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
                    .setAudioAttributes(
                        AudioAttributes.Builder()
                            .setUsage(AudioAttributes.USAGE_MEDIA)
                            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                            .build()
                    )
                    .setAcceptsDelayedFocusGain(true)
                    .setOnAudioFocusChangeListener { focus ->
                        when (focus) {
                            AudioManager.AUDIOFOCUS_GAIN -> {
                            }
                            else -> stopSelf()
                        }
                    }.build()

            when (audioManager.requestAudioFocus(focusRequest)) {
                AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> speak(audioManager, focusRequest)
                AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> stopSelf()
                AudioManager.AUDIOFOCUS_REQUEST_FAILED -> stopSelf()
            }

        } else {
            val result = audioManager.requestAudioFocus( { focusChange: Int ->
                when(focusChange) {
                    AudioManager.AUDIOFOCUS_GAIN -> {
                    }
                    else -> stopSelf()
                }
            },
                AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
            )

            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                speak(audioManager, null)
            }
        }
    }

    private fun speak(audioManager: AudioManager, focusRequest: AudioFocusRequest?) {
        val speechListener = object : UtteranceProgressListener() {
            override fun onStart(utteranceId: String?) {
                Log.d(TAG, "onStart: Started syntheses.....")
            }

            override fun onDone(utteranceId: String?) {
                Log.d(TAG, "onDone: Completed synthesis ")
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && focusRequest != null) {
                    audioManager.abandonAudioFocusRequest(focusRequest)
                }
                stopSelf()
            }

            override fun onError(utteranceId: String?) {
                Log.d(TAG, "onError: Error synthesis")
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && focusRequest != null) {
                    audioManager.abandonAudioFocusRequest(focusRequest)
                }
                stopSelf()
            }
        }
        val paramsMap: HashMap<String, String> = HashMap()
        paramsMap[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = "tts_service"

        tts?.speak(spokenText, TextToSpeech.QUEUE_ADD, paramsMap)
        tts?.setOnUtteranceProgressListener(speechListener)
    }

    override fun onDestroy() {
        if (tts != null) {
            Log.d(TAG, "onDestroy: destroyed tts")
            tts?.stop()
            tts?.shutdown()
        }
        super.onDestroy()
    }

    override fun onBind(arg0: Intent?): IBinder? {
        return null
    }
    
    companion object {
        private const val TAG = "TTS_Service"
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.