Я работаю над приложением, которое должно продолжать читать вслух текст после выключения экрана. Для достижения этой цели я поместил код преобразования текста в речь (TTS) в службу переднего плана, чтобы TTS мог продолжать работать, когда экран выключен.
Раньше на моем телефоне работало хорошо. Но после того, как я обновил свой телефон с Android 11 до Android 12, TTS перестает работать после выключения экрана на некоторое время, обычно через несколько минут.
Обычно после того, как TTS заканчивает произносить одно предложение, он вызывает метод onDoneUtteranceProgressListener, поэтому я могу заставить TTS произнести следующее предложение. Причина, по которой TTS перестает работать, заключается в том, что метод onDone перестает вызываться после выключения экрана на некоторое время. Он не прекращается сразу, а прекращается через несколько минут, иногда дольше, иногда короче.
Я предположил, что оптимизация батареи новой ОС Android вызывает эту проблему. Но после того, как я отключил оптимизацию батареи системы, она тоже не работает. Я также заметил, что некоторые похожие приложения имеют ту же проблему, а некоторые нет. Как я могу решить эту проблему?
Этот код работает в Android 12, даже если приложение работает в фоновом режиме.
класс 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"
}
}
Сегодня я провел дополнительные тесты и обнаружил, что мне также нужно добавить setAudioAttributes() в свой код, чтобы предотвратить остановку TTS. Кажется, что есть несколько причин, по которым TTS останавливается, когда экран выключен.
Объект TTS должен быть создан только в onCreate(), иначе он остановится после выключения экрана на некоторое время.
Я обнаружил, что раньше не отключал оптимизацию батареи. После того, как я отключил оптимизацию батареи, проблема решилась. Теперь я могу хранить CountDownTimer и создавать TTS в любом месте. Хороший стиль кодирования может продлить жизнь TTS, но не навсегда, если оптимизация батареи слишком агрессивна. Этот пост - способ действительно отключить оптимизацию батареи. Проверьте, включена ли оптимизация батареи для приложения.
Я сравнил ваш код и нашел два ключевых отличия. Вы реализуете
OnInitListenerв классе и ставитеthisвторым параметромTextToSpeech(this, this). Я напрямую создаю объект в круглых скобках функции. Во-вторых, вы сначала создаетеUtteranceProgressListenerи вставляете его вsetOnUtteranceProgressListener(). Я напрямую создаю объект в круглых скобках функции. Я меняю стиль кодирования, чтобы он был похож на ваш, и мне также нужно удалитьCountDownTimer, который также работает в службе переднего плана. Теперь он работает отлично. Спасибо за помощь!