Android 熄屏一段时间后TTS停止朗读
Android TTS stops reading aloud after the screen is turned off for a while
我正在开发一个需要在屏幕关闭后继续大声朗读文本的应用程序。为了达到这个目的,我把文字转语音(TTS)的代码放在了Foreground Service中,这样TTS可以在屏幕关闭时保持运行。
它之前在我的 phone 上运行良好。但是我把我的phone从Android11升级到Android12后,TTS会在屏幕关闭一段时间后停止工作,通常是几分钟后。
一般情况下,TTS说完一句话后,会调用UtteranceProgressListener
的onDone
方法,这样我就可以让TTS在那里说下一句了。 TTS 停止工作的原因是 onDone
方法在屏幕关闭一段时间后停止调用。它不会立即停止,而是在几分钟后停止,有时更长,有时更短。
我猜是新的AndroidOS的电池优化导致了这个问题。但是我关闭系统电池优化后,它也不起作用。我还注意到一些类似的应用程序有同样的问题,但有些应用程序没有。我该如何解决这个问题?
此代码在 Android 12 中运行,即使应用程序在后台运行
class TTS:服务(),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"
}
}
我正在开发一个需要在屏幕关闭后继续大声朗读文本的应用程序。为了达到这个目的,我把文字转语音(TTS)的代码放在了Foreground Service中,这样TTS可以在屏幕关闭时保持运行。
它之前在我的 phone 上运行良好。但是我把我的phone从Android11升级到Android12后,TTS会在屏幕关闭一段时间后停止工作,通常是几分钟后。
一般情况下,TTS说完一句话后,会调用UtteranceProgressListener
的onDone
方法,这样我就可以让TTS在那里说下一句了。 TTS 停止工作的原因是 onDone
方法在屏幕关闭一段时间后停止调用。它不会立即停止,而是在几分钟后停止,有时更长,有时更短。
我猜是新的AndroidOS的电池优化导致了这个问题。但是我关闭系统电池优化后,它也不起作用。我还注意到一些类似的应用程序有同样的问题,但有些应用程序没有。我该如何解决这个问题?
此代码在 Android 12 中运行,即使应用程序在后台运行
class TTS:服务(),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"
}
}