Programmation Java Swing/Gestion des actions asynchrones

La création d'une interface graphique pour une application en Java entraîne la création de threads pour gérer les tâches de fond comme la gestion des évènements qui créer une boucle de réception d'évènements et leur distribution aux composants visibles.

Les actions déclenchées par l'utilisateur peuvent prendre beaucoup de temps : recherche de fichiers, calculs complexes, ... Lorsque de telles actions sont appelées directement depuis les écouteurs d'évènements, l'interface graphique est bloquée durant les secondes ou minutes d'exécution. Pour éviter ce problème, il faut gérer correctement les appels à ces actions de manière asynchrone.

Action asynchrone

modifier

Pour éviter qu'une action dont l'exécution est longue empêche l'utilisateur d'utiliser l'interface graphique, il faut créer un thread spécifique.

Exemple : Une action longue exécutée par clic sur un bouton :

JButton b_action = new JButton("Démarrer l'action")
b_action.addActionListener(new ActionListener()
	{
		public void actionPerformed(ActionEvent e) { demarrerAction(); }
	});

La méthode demarrerAction() crée et démarre un thread pour exécuter l'action et retourne sans attendre la fin d'exécution afin de ne pas bloquer l'interface graphique :

private void demarrerAction()
{
	new Thread(run_action_longue, "longue-action").start();
}

// L'action du thread : appeler la méthode actionLongue()
Runnable run_action_longue = new Runnable()
{
	@Override
	public void run()
	{ actionLongue(); }
};

private void actionLongue()
{
	// Ici l'action longue...

	// Pour tester, attendre 10 secondes :
	try{ Thread.sleep(10000); }
	catch(InterruptedException ex){ }
}

Mise à jour de l'interface

modifier

Une action asynchrone peut avoir besoin de mettre à jour l'interface graphique : indiquer que l'action est terminée, afficher le fichier ou l'image chargée ou le résultat d'un calcul, ... Pour cela, les méthodes des composants doivent être appelées par le thread de gestion des évènements afin d'éviter les conflits. Il suffit pour cela d'appeler la méthode statique invokeLater de la classe javax.swing.SwingUtilities.

private void actionLongue()
{
	// Ici l'action longue...
	// resultat = ...

	// Afficher le résultat :
	SwingUtilities.invokeLater(new Runnable()
	{
		@Override
		public void run()
		{ afficher(resultat); }
	});
}