Depuis une vingtaine d’années, les applications sont de plus en plus dotées d’interfaces graphiques, les GUI. Celles-ci ont été une révolution pour l’utilisation des machines. Cela dit, pour nous, développeurs, cela a introduit de nouvelles pratiques.
Débloque les + belles offres tech en 10 mins
En fait, un toolkit graphique fonctionne généralement dans un thread dédié, l’Event-Dispatch-Thread en Swing. Il fonctionne de la manière suivante en boucle infinie :
- Recevoir du système d’exploitation les événements utilisateur tels que les clics de souris ou les entrées clavier.
- Notifier les composants de la vue qui ont reçu les événements (d’où le « dispatch » d’Event Dispatch Thread) de façon à ce que chacun d’eux déclenche le gestionnaire correspondant.
- Rafraîchir l’affichage.
Ces étapes se font en réalité plus ou moins simultanément, en particulier la première et la deuxième. Cela dit, ce fonctionnement a une implication : un gestionnaire d’événement d’un composant ne doit pas prendre longtemps à s’exécuter, quelques millisecondes tout au plus, sinon il empêche le rafraîchissement de la fenêtre. C’est comme ça que Windows par exemple peut afficher qu’un programme ne répond pas, parce qu’il est bloqué dans un gestionnaire d’événement.
J’ai un traitement long à lancer quand l’utilisateur clique sur un bouton, comment faire ?
C’est là que les threads interviennent. Ceux-ci permettent en effet de lancer de manière asynchrone un traitement. Autrement dit le fait que le nouveau thread lancé fasse un calcul intensif n’empêche pas le rafraîchissement de la GUI.
Néanmoins, il faut faire très attention avec les threads, car les toolkits graphiques ne sont généralement pas thread-safe, hormis certaines méthodes explicitement documentées comme telles. Ce billet explique pourquoi. Ceci peut se traduire par des affichages corrompus ou tout simplement des fenêtres qui ne s’affichent pas, si vous n’y prenez pas garde.
Dès lors, pour rafraichir un composant graphique depuis un thread, il convient d’utiliser les méthodes ou objets suivants :
- En Swing, il faut utiliser SwingUtilities.invokeLater() dans le cas général, ou SwingUtilities.invokeAndWait() si vous désirez une réponse de l’utilisateur avant de poursuivre un traitement. A noter que depuis Java 6.0 vous avez aussi les SwingWorker qui peuvent aider mais qui n’est pas applicable dans tous les cas.
- En SWT, il faut utiliser Display.getDefault().asyncExec() de façon à rafraîchir l’affichage.
- Enfin, en JavaFX 2.0, qui est le successeur de Swing, il faut utiliser Platform.invokeLater().
Rafraîchir une GUI sur un objet en cours de calcul
Ayant déjà été confronté au problème dans mon jeu de Puissance 4 je vous partage une astuce qui peut vous servir si vous voulez rafraîchir votre GUI avec un objet en cours de calcul via une méthode du type SwingUtilities.invokeLater() : transférez à votre toolkit graphique une copie de votre objet et non l’original lui-même. ne copie par référence ne suffit pas, il faut une copie en profondeur avec un constructeur par recopie ou la méthode clone(). Si vous ne le faites pas, rien ne vous garantit qu’entre le moment où vous aurez soumis l’objet à votre toolkit graphique et le moment où il sera pris en compte pour l’affichage son état sera resté le même…
Cet article vous a plu ? Vous aimerez sûrement aussi :
- De la nécessité d’utiliser des structures de données thread safe en environnement multithread
- Gestion des threads en Java
Débloque les + belles offres tech en 10 mins
Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.
Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.