Aller au contenu

TextToSpeech et rotation [résolu]


Invité hardrocky

Recommended Posts

Invité hardrocky

Bonsoir,

Mon application implémente la synthèse vocale. Dans onstop et onDestroy je fais un tts.stop() au cas où l'application perdrait le focus, pour que la synthèse se stoppe. Cependant, la synthèse est également coupée lors de la rotation de l'écran. J'aimerais que ça ne soit pas le cas.

Comment gérer cela ? A noter que j'ai un layout différent pour le mode portrait et paysage, et que le contenu de mon EditText doit être conservé lors de la rotation.

Je ne suis pas sûr que android:configChanges="orientation|screenSize" soit la meilleure solution.

Merci de votre aide :)

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

J'y ai pensé mais ce n'est vraiment pas top pour l'utilisateur qui aura l'impression que l'app est bloquée ou plantée.

Lien vers le commentaire
Partager sur d’autres sites

Le android:configChanges n'est effectivement pas la solution pour toi, vu que ton Activity doit être supprimée pour pouvoir être recréée avec le nouveau layout. Pour gérer ce cas, et garder des infos entre les deux instances de ton Activity, tu peux implémenter la méthode onRetainNonConfigurationInstance, qui sera appelée avant destruction de la première instance, et qui mettra à disposition de la nouvelle instance (via getLastNonConfigurationInstance) les infos sauvegardées.

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

Donc je suis sensé gardé mon instance de TextToSpeech je suppose. Cependant, j'ai oublié de mentionner que je coupe la synthèse dans onstop et onDestroy pour que la diction s'interrompe lorsque l'application perd le focus (retour sur le homescreen par exemple).

@Override
public void onDestroy() { // Coupe la synthèse quand on ferme l'application avec "back"
	// Pour la synthèse
	if (tts != null) {
		tts.stop();
		tts.shutdown(); // Détruit définitivement
	}
	super.onDestroy();
}

@Override
public void onstop() { // Coupe la synthèse quand on réduit l'application avec "home"
	if (tts != null) {
		tts.stop();
	}
	super.onstop();
}

Donc quand on tourne le tel, ces deux méthodes sont pourtant appelées. Comment gérer tout ça ?

Merci beaucoup pour ton aide !

Lien vers le commentaire
Partager sur d’autres sites

Le problème dans ton cas, c'est que la méthode onRetainNonConfigurationInstance est appelée entre l'appel à onstop et celui à onDestroy. Donc, quand elle est appelée, onstop a déjà arrêté le TTS. Et pas moyen de différencier l'appel à onstop qui sera suivi d'une appel à onRetainNonConfigurationInstance de celui qui ne sera pas suivi par un tel appel...

Je ne vois pas de solution simple. Eventuellement, une idée (mais ça me semble du bidouillage...): dans le onstop, au lieu d'arrêter le TTS, tu positionnes une alarme (genre 500 ms plus tard), qui quand elle sera déclenchée arrêtera le TTS; et dans le onRetainNonConfigurationInstance, tu annules cette alarme...

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

C'est une solution envisageable oui mais le fait d'attendre une demi seconde ne me convient pas tellement. L'utilisateur aura l'impression d'une ralentissement, la synthèse ne sera pas coupée immédiatement dans le cas d'un retour sur le homescreen.

Lorsque onstop() est appelé, n'y a-t-il pas moyen de distinguer une "réduction" de l'application vers le background d'un changement de configuration ?

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

Exactement ce que je cherchais merci beaucoup ! J'implémente ça ce soir, je te dis si ça a fonctionné.

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

Me revoilà ! Bon du coup, petit compte de rendu de mes tests.

J'ai du faire pas mal d'adaptations dans le code (d'ailleurs il est sur un SVN public https://subversion.i...inActivity.java) mais ça fonctionne. Seulement voilà, mon logcat est très étrange...

03-21 00:11:50.342: E/ActivityThread(16920): Activity eu.romainpellerin.handicapp.MainActivity has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41a4b040 that was originally bound here
03-21 00:11:50.342: E/ActivityThread(16920): android.app.ServiceConnectionLeaked: Activity eu.romainpellerin.handicapp.MainActivity has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41a4b040 that was originally bound here
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:969)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:863)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ContextImpl.bindService(ContextImpl.java:1418)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ContextImpl.bindService(ContextImpl.java:1407)
03-21 00:11:50.342: E/ActivityThread(16920): at android.content.ContextWrapper.bindService(ContextWrapper.java:473)
03-21 00:11:50.342: E/ActivityThread(16920): at android.speech.tts.TextToSpeech.connectToEngine(TextToSpeech.java:627)
03-21 00:11:50.342: E/ActivityThread(16920): at android.speech.tts.TextToSpeech.initTts(TextToSpeech.java:597)
03-21 00:11:50.342: E/ActivityThread(16920): at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:553)
03-21 00:11:50.342: E/ActivityThread(16920): at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:527)
03-21 00:11:50.342: E/ActivityThread(16920): at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:512)
03-21 00:11:50.342: E/ActivityThread(16920): at eu.romainpellerin.handicapp.MainActivity.onCreate(MainActivity.java:84)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.Activity.performCreate(Activity.java:5104)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ActivityThread.access$600(ActivityThread.java:141)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
03-21 00:11:50.342: E/ActivityThread(16920): at android.os.Handler.dispatchMessage(Handler.java:99)
03-21 00:11:50.342: E/ActivityThread(16920): at android.os.Looper.loop(Looper.java:137)
03-21 00:11:50.342: E/ActivityThread(16920): at android.app.ActivityThread.main(ActivityThread.java:5041)
03-21 00:11:50.342: E/ActivityThread(16920): at java.lang.reflect.Method.invokeNative(Native Method)
03-21 00:11:50.342: E/ActivityThread(16920): at java.lang.reflect.Method.invoke(Method.java:511)
03-21 00:11:50.342: E/ActivityThread(16920): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-21 00:11:50.342: E/ActivityThread(16920): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-21 00:11:50.342: E/ActivityThread(16920): at dalvik.system.NativeStart.main(Native Method)

Si tu as (ou quelqu'un d'autre) une idée... M'enfin bon, ça fonctionne quand même.

Et j'ai un joli Force Close dans un seul cas : quand je lance l'appli, je la tourne, et je la ferme (sans avoir utilisé la synthèse vocale).

03-21 00:19:38.881: E/AndroidRuntime(17340): FATAL EXCEPTION: main

03-21 00:19:38.881: E/AndroidRuntime(17340): java.lang.RuntimeException: Unable to destroy activity {eu.romainpellerin.handicapp/eu.romainpellerin.handicapp.MainActivity}: java.lang.IllegalArgumentException: Service not registered: android.speech.tts.TextToSpeech$Connection@41ac8ca8

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

Bon, après quelques heures passées à essayer de comprendre d'où venait ces erreurs et ce force close, j'ai fini par abandonner cette méthode et je me suis tournée vers android:configChanges="orientation|screenSize"

Ca a été relativement simple à implémenter. J'appelle une fonction initialisation() dans le onCreate et onConfigurationchanged, qui inflate le layout, instancie les différents boutons, et met un listener (addTextChangedListener()) sur le EditText de manière à récuperer ce que l'utilsateur à entré au clavier entre deux inflate de layout. Et c'est à peu près tout.

Le code source complet est visible ici (https://subversion.iut-nantes.univ-nantes.fr/~info1-2012-gU/HandicApp2/src/eu/romainpellerin/handicapp/MainActivity.java)

Merci beaucoup pour ton aide chpil en tout cas !

Lien vers le commentaire
Partager sur d’autres sites

Le Logcat disait que ton Activity ne se désenregistrait pas correctement de auprès d'un service. En fait c'est lié au TTS, qui utilise un/des Services (on ne le voit pas vraiment).

J'ai un peu regardé ton code; et je pense que tu dois te retrouver dans un cas où un TTS est recréé (dans onCreate) alors que le précédent n'a pas été détruit (dans onDestroy); je n'arrive pas à faire le lien entre tes conditions d'arrêt du TTS dans onstop/onDestroy et celle de création dans onCreate, elles ne me paraissent pas cohérentes.

Tu aurais du utiliser les méthodes onRetainNonConfigurationInstance/getLastNonConfigurationInstance pour passer l'état (ton TTS en gros) entre les deux instances de l'Activity lors d'un changement de configuration

Et surtout ne pas utiliser des attributs statiques pour gérer cela (l'utilisation d'attributs statique, en règle générale, et plus particulièrement dans Android où les instances sont managées par le système, est à proscrire, c'est mauvais pour la santé...)

Lien vers le commentaire
Partager sur d’autres sites

Invité hardrocky

je pense que tu dois te retrouver dans un cas où un TTS est recréé (dans onCreate) alors que le précédent n'a pas été détruit (dans onDestroy);

1) Le seul cas où l'activity peut être recréé serait pour un changement de configuration autre que orientation|screenSized

Lors d'un changement de langue du système par exemple, l'activity est recréée. Mais du coup j'ai testé, et ca ne pose pas de problème. Ca fonctionne parce que...

Dans onCreate

if ((!active) && (savedInstanceState != null)) {
bouton_synthese.setEnabled(false);  // Désactive le bouton
}
else {
tts = new TextToSpeech(this, this);
}

active est un booleen qui vaut faux quand le moteur de synthèse n'a pas été correctement initialisé (ou qu'il n'y a pas la langue souhaitée). A l'instanciation de l'activity, ca vaut vrai donc l'instanciation se fait. C'est vrai que c'est condition est inutile je vais la virer.

Quant au onstop/onDestroy,

if (tts != null) {
tts.stop();
tts.shutdown();
}

L'objet tts est détruit tout le temps, sauf s'il ne correspond à aucune instance (quand il vaut null quoi). Donc aucun risque de récréer un TTS alors qu'il y en a un qui existe. Je me trompe ?

2) De quels attribut statiques parles-tu ? De "active" ?

Lien vers le commentaire
Partager sur d’autres sites

Je faisais référence à l'ancienne version de ton code, où tu ne gérais pas toi-même le changement d'orientation. Mes remarques ne s'appliquent pas à ta dernière version...

(Nos posts se sont croisés, j'avais commencé à rédiger le mien avant que tu ne donnes ta solution, mais tu étais repassé par là avant que je n'ai eu le temps de poster...)

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...