Evitando el retraso de inicialización de Google TTS Engine en Android

He intentado jugar el objeto de TextToSpeech cuando un acontecimiento específico se activa en el teléfono.

Sin embargo, enfrento problemas con el motor predeterminado de Google TTS que está instalado en la mayoría de los teléfonos. A partir de ahora, estoy jugando un poco de texto inmediatamente después de que se inicialice el objeto TextToSpeech, y cerrando el recurso tan pronto como termine el discurso, como por el código siguiente:

public class VoiceGenerator { private Context context = null; private static TextToSpeech voice = null; public VoiceGenerator(Context context) { this.context = context; } public void voiceInit(String text) { try { if (voice == null) { new Thread(new Runnable() { @Override public void run() { voice = new TextToSpeech(context, new TextToSpeech.OnInitListener() { @Override public void onInit(final int status) { try { if (status != TextToSpeech.ERROR) { voice.setLanguage(Locale.US); Log.d("VoiceTTS", "TTS being initialized"); HashMap p = new HashMap<String, String>(); p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance"); //Speaking here voice.speak(text, TextToSpeech.QUEUE_ADD, p); voice.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) { } @Override public void onDone(String utteranceId) { Log.d("VoiceTTS", "TTS being released"); clearTtsEngine(); } @Override public void onError(String utteranceId) { } }); } } catch (Exception e) { clearTtsEngine(); Log.d("ErrorLog", "Error occurred while voice play"); e.printStackTrace(); } } }); } }).start(); } } catch(Exception e) { clearTtsEngine(); Log.d("ErrorLog","Error occurred while voice play"); e.printStackTrace(); } } public static void clearTtsEngine() { if(voice!=null) { voice.stop(); voice.shutdown(); voice = null; } } } 

Sin embargo, el problema que estoy enfrentando es la cantidad finita de retraso asociado con inicializar el motor de Google TTS – cerca de 6-8 segundos en mis dispositivos .

He leído en otras publicaciones que este retraso puede evitarse utilizando otros motores TTS. Dado que siempre desarrollo en mi teléfono Samsung, que tiene su propio TTS propietario configurado por defecto, nunca me he dado cuenta de este problema hasta que revisé mi aplicación en otros teléfonos de marca que tiene el motor de Google TTS configurado como predeterminado. Pero, idealmente no quiero obligar a los usuarios a instalar otra aplicación junto con la mía, y por lo tanto, me gustaría que esto funcione con el motor predeterminado de Google TTS en sí.

A través de alguna codificación errónea que más tarde rectificé, me di cuenta de que si pudiera mantener el objeto TextToSpeech inicializado de antemano y siempre no nulo – una vez inicializado , podría aparentemente pasar por alto este retraso.

Sin embargo, dado que hay una necesidad de apagar el recurso una vez que hayamos terminado con él, no soy capaz de mantener el objeto vivo e inicializado por mucho tiempo, y no sé cuándo inicializar / apagar el recurso, ya que técnicamente necesito el Voz para reproducir en cualquier momento el evento específico ocurre , lo que en su mayoría sería cuando mi aplicación no está abierta en el teléfono.

Así que mis preguntas son las siguientes:

  1. ¿Podemos de alguna manera reducir o eliminar el retraso de inicialización de Google TTS Engine, programáticamente o de otra manera?

  2. ¿Hay alguna manera a través de la cual puedo mantener el objeto TextToSpeech vivo e inicializado en todo momento como decir, a través de un servicio? ¿O este sería un diseño malo, que consume muchos recursos?

  3. También está utilizando un objeto TextToSpeech estático el camino correcto para ir, para mis necesidades?

Cualquier solución junto con código sería apreciada.

Actualización: He confirmado que el retraso se asocia exclusivamente con el motor Google TTS, como he intentado utilizar otros motores TTS gratuitos y pagados, donde hay poco o ningún retraso. Pero todavía preferiría no tener dependencias de terceros, si es posible, y me gustaría hacer que esto funcione con Google TTS Engine.

ACTUALIZACIÓN: He aparentemente omitido este problema vinculando este objeto TTS a un servicio y accediéndolo desde el servicio. El servicio es STICKY (si el servicio termina debido a un problema de memoria, Android OS reiniciará el servicio cuando la memoria esté disponible de nuevo) y está configurado para reiniciarse al reiniciar el dispositivo.

El servicio sólo inicializa el objeto TTS y no realiza ningún otro trabajo. No estoy deteniendo explícitamente el servicio, permitiendo que funcione el mayor tiempo posible. He definido el objeto TTS como una estática, para que pueda acceder a ella desde otras clases de mi aplicación.

Aunque esto parece estar funcionando increíblemente bien, me preocupa si esto podría conducir a problemas de memoria o batería (en mi situación específica donde el servicio sólo maneja la inicialización del objeto y luego permanece latente). ¿Hay algún problema en mi diseño, o se pueden hacer más mejoras / comprobaciones para mi diseño?

Archivo de manifiesto:

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <application android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name="activity.MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="services.BroadcastReceiverOnBootComplete" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED" /> <data android:scheme="package" /> </intent-filter> </receiver> <service android:name="services.TTSService"></service> 

Código del BroadcastReceiver:

 public class BroadcastReceiverOnBootComplete extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) { Intent serviceIntent = new Intent(context, TTSService.class); context.startService(serviceIntent); } } 

}

TTSService código:

 public class TTSService extends Service { private static TextToSpeech voice =null; public static TextToSpeech getVoice() { return voice; } @Nullable @Override public IBinder onBind(Intent intent) { // not supporting binding return null; } public TTSService() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { try{ Log.d("TTSService","Text-to-speech object initializing"); voice = new TextToSpeech(TTSService.this,new TextToSpeech.OnInitListener() { @Override public void onInit(final int status) { Log.d("TTSService","Text-to-speech object initialization complete"); } }); } catch(Exception e){ e.printStackTrace(); } return Service.START_STICKY; } @Override public void onDestroy() { clearTtsEngine(); super.onDestroy(); } public static void clearTtsEngine() { if(voice!=null) { voice.stop(); voice.shutdown(); voice = null; } } } 

Código de VoiceGenerator modificado:

 public class VoiceGenerator { private TextToSpeech voice = null; public VoiceGenerator(Context context) { this.context = context; } public void voiceInit(String text) { try { if (voice == null) { new Thread(new Runnable() { @Override public void run() { voice = TTSService.getVoice(); if(voice==null) return; voice.setLanguage(Locale.US); HashMap p = new HashMap<String, String>(); p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance"); voice.speak(text, TextToSpeech.QUEUE_ADD, p); voice.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) { } @Override public void onDone(String utteranceId) { } @Override public void onError(String utteranceId) { } }); } }).start(); } } catch(Exception e) { Log.d("ErrorLog","Error occurred while voice play"); e.printStackTrace(); } } } 

Soy el desarrollador de la aplicación de Android total! Eso no es un tapón desvergonzado, es para demostrar que uso el patrón de diseño que estás considerando y que he pasado por lo que ha provocado tu pregunta.

Es fresco en mi mente, ya que he pasado el último año reescribiendo mi código y tuve que dar gran consideración a la cuestión circundante.

  • ¿Podemos de alguna manera reducir o eliminar el retraso de inicialización de Google TTS Engine, programáticamente o de otra manera?

Hace un tiempo hice una pregunta similar e inicializar el texto en un objeto de voz en un hilo de fondo donde no está compitiendo con otras tareas, puede reducir el retardo ligeramente (como veo que ya está haciendo en su código publicado).

También puede asegurarse de que la solicitud de voz no se retrasa más al seleccionar una voz incrustada, en lugar de una dependiente de una red:

En API 21+ echa un vistazo a las opciones de la clase Voz . Particularmente getFeatures () donde usted puede examinar la latencia y el requisito para una red.

En API <21 – Defina KEY_FEATURE_NETWORK_SYNTHESIS como false dentro de sus parámetros.

Independientemente de lo anterior, el motor de Google TTS tiene el tiempo de inicialización más largo de cualquiera de los motores que he probado (todos ellos creo). Creo que esto es simplemente porque están utilizando todos los recursos disponibles en el dispositivo para ofrecer la más alta calidad de voz que puedan.

De mi propia prueba personal, este retraso es directamente proporcional al hardware del dispositivo. Cuanto más RAM y el rendimiento del procesador, menor será el tiempo de inicialización. Lo mismo se dijo para el estado actual del dispositivo – Creo que encontrará que después de un reinicio, donde hay memoria libre y Android no tendrá que matar a otros procesos, el tiempo de inicialización se reducirá.

En resumen, aparte de lo mencionado anteriormente, no , no se puede reducir el tiempo de inicialización.

  • ¿Hay alguna manera a través de la cual puedo mantener el objeto TextToSpeech vivo e inicializado en todo momento como decir, a través de un servicio? ¿O este sería un diseño malo, que consume muchos recursos?

  • También está utilizando un objeto TextToSpeech estático el camino correcto para ir, para mis necesidades?

Como usted ha notado, una manera de evitar el tiempo de la inicialización, es permanecer atado al motor. Sin embargo, hay otros problemas que usted puede desear considerar antes de hacer esto.

Si el dispositivo está en un estado en el que necesita liberar recursos, que es el mismo que provoca un retraso de inicialización prolongado, Android está bien dentro de sus derechos de recolección de basura de este enlace. Si mantiene esta vinculación en un servicio de fondo, el servicio puede ser eliminado, poniéndolo de nuevo en el cuadrado uno.

Además, si permanece vinculado al motor, los usuarios verán el uso de memoria colectiva en la configuración de la aplicación en ejecución de Android. Para muchos, muchos usuarios que consideran incorrectamente el uso de memoria (latente) directamente proporcional al drenaje de la batería, por mi experiencia, esto causará desinstalaciones y calificaciones de aplicaciones pobres.

En el momento de escribir esto, Google TTS está vinculado a mi aplicación a un costo de 70mb.

Si todavía desea continuar sobre esta base, puede intentar conseguir que Android priorice su proceso y lo mate por última vez. Hará esto utilizando un servicio de Foreground. Esto abre otra lata de gusanos sin embargo, que no voy a entrar.

Efectivamente, la vinculación al motor en un servicio y la comprobación de que el servicio está funcionando cuando usted quiere que el motor para hablar, es un patrón de singleton. Hacer el motor estático dentro de este servicio no serviría a ningún propósito que pueda pensar.

Por último , para compartir mi experiencia en cuanto a cómo he tratado con lo anterior.

Tengo ' Google es lento para inicializar ' en la parte superior de mis 'errores conocidos' y 'FAQ' en la aplicación.

Monitoreo el tiempo que tarda el motor en llamar onInit . Si tarda demasiado tiempo, hago una notificación al usuario y la dirijo a las preguntas más frecuentes, donde se les aconseja suavemente que prueben otro motor TTS.

Ejecuto un temporizador de fondo, que libera el motor después de un período de inactividad. Esta cantidad de tiempo es configurable por el usuario y viene con avisos de retraso de inicialización …

Sé que lo anterior no soluciona tus problemas, pero quizás mis sugerencias apaciguen a tus usuarios, lo cual es un segundo distante para resolver el problema, pero bueno …

No tengo duda de que Google aumentará gradualmente el rendimiento de la inicialización – Hace cuatro años, estaba teniendo este problema con IVONA, que finalmente hizo un buen trabajo en su tiempo de inicialización.

  • Obtener la visualización de la ventana desde el servicio de Android
  • Null intent redelivered to Service onStartCommand ()
  • Servicio AIDL que no se conecta después de bindService ()
  • Programación de salida HDMI para pantalla dual
  • Ejecución de WebView en segundo plano
  • Servicio persistente
  • ¿Debo usar Service o IntentService para mi aplicación android?
  • Android RuntimeException: No se puede crear una instancia del servicio
  • Servicio de ventana superpuesta en Android
  • Incapaz de crear servicio java.lang.NullPointerException
  • ¿Cómo seguir reproduciendo música en segundo plano después de que el usuario haga desaparecer la aplicación?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.