48 votes

Comment utiliser SwingWorker en Java ?

En rapport avec ma question précédente : Appeler repaint depuis une autre classe en Java ?

Je suis novice en Java et j'ai regardé quelques tutoriels sur SwingWorker. Pourtant, je ne sais pas comment le mettre en œuvre avec le code d'exemple que j'ai donné dans la question précédente.

Quelqu'un peut-il m'expliquer comment utiliser SwingWorker par rapport à mon extrait de code et/ou m'indiquer un tutoriel décent ? J'ai regardé mais je ne suis pas sûr de comprendre.

112voto

coobird Points 70356

En général, SwingWorker est utilisé pour effectuer des tâches de longue durée dans Swing.

L'exécution de tâches longues sur l'Event Dispatch Thread (EDT) peut provoquer le blocage de l'interface graphique. SwingUtilities.invokeLater et invokeAndWait qui permet à l'interface graphique de rester réactive en donnant la priorité aux autres événements AWT avant d'exécuter la tâche souhaitée (sous la forme d'une commande Runnable ).

Cependant, le problème avec SwingUtilities c'est qu'il ne permettait pas de renvoyer des données à partir de l'exécution. Runnable à la méthode originale. C'est ainsi que SwingWorker a été conçu pour répondre aux besoins.

Le tutoriel Java comporte une section sur SwingWorker .

Voici un exemple où un SwingWorker est utilisé pour exécuter une tâche fastidieuse sur un thread séparé, et affiche une boîte de message une seconde plus tard avec la réponse.

Tout d'abord, une classe étendant SwingWorker sera faite :

class AnswerWorker extends SwingWorker<Integer, Integer>
{
    protected Integer doInBackground() throws Exception
    {
        // Do a time-consuming task.
        Thread.sleep(1000);
        return 42;
    }

    protected void done()
    {
        try
        {
            JOptionPane.showMessageDialog(f, get());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Le type de retour de la fonction doInBackground et get sont spécifiées comme le premier type de l'élément SwingWorker et le second type est le type utilisé pour le retour de l'objet publish et process qui ne sont pas utilisées dans cet exemple.

Ensuite, afin d'invoquer le SwingWorker le execute est appelée. Dans cet exemple, nous allons accrocher un ActionListener à un JButton pour exécuter le AnswerWorker :

JButton b = new JButton("Answer!");
b.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e)
    {
        new AnswerWorker().execute();
    }
});

Le bouton ci-dessus peut être ajouté à un JFrame et cliqué dessus pour obtenir une boîte de message une seconde plus tard. Les éléments suivants peuvent être utilisés pour initialiser l'interface graphique d'une application Swing :

private void makeGUI()
{
    final JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().setLayout(new FlowLayout());

    // include: "class AnswerWorker" code here.
    // include: "JButton" b code here.

    f.getContentPane().add(b);
    f.getContentPane().add(new JButton("Nothing"));
    f.pack();
    f.setVisible(true);
}

Une fois l'application lancée, il y aura deux boutons. L'un intitulé "Répondre !" et l'autre "Rien". Lorsque l'on clique sur le bouton "Answer !", rien ne se passe au début, mais si l'on clique sur le bouton "Nothing", cela fonctionne et démontre que l'interface graphique est réactive.

Et, une seconde plus tard, le résultat de la AnswerWorker apparaîtra dans la boîte de message.

0 votes

Je ne crois pas que ce que je fais soit une tâche de longue haleine. J'ai pensé à une autre façon de traiter ce problème, et je la poserai dans une autre question. Merci

0 votes

Waw ! Très instructif pour moi aussi. Mais qu'en est-il si j'ai plusieurs requêtes qui retournent des données à mes composants Swing (GUI) ? Dois-je recréer toutes les classes SwingWorker à chaque appel ? Ou y a-t-il une alternative en utilisant Thread.daemon() @coobird ? :D

1 votes

@gumuruh (Si je vous comprends bien) Selon votre cas d'utilisation, avoir un fil de fond peut être une bonne option, mais cela nécessiterait plus d'informations, ce qui pourrait être une nouvelle question à part entière. Cependant, une chose que l'on peut dire est que, selon la documentation, une instance de SwingWorker est conçu pour avoir son execute n'est appelée qu'une seule fois, de sorte qu'une nouvelle exécution de la méthode execute ne serait pas une option.

12voto

dalvarezmartinez1 Points 249

Acceptez :

L'exécution de tâches longues sur le fil de distribution d'événements (EDT) peut provoquer le blocage de l'interface graphique.

Je ne suis pas d'accord :

Ainsi, l'une des choses qui ont été faites est d'utiliser SwingUtilities.invokeLater et invokeAndWait qui maintient l'interface graphique réactive

invokeLater exécute toujours le code sur l'EDT, et peut geler votre interface utilisateur ! Essayez ceci :

SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

En tout cas, moi, je ne peux pas déplacer ma souris une fois que j'ai cliqué sur le bouton qui déclenche l'actionPerformed avec le code ci-dessus. Est-ce que quelque chose m'échappe ?

1 votes

Vous avez raison. En général, invokeLater sera appelé en dehors du thread de distribution d'événements pour exécuter quelque chose sur le thread de distribution d'événements, par exemple une tâche longuement exécutée peut appeler invokeLater pour modifier l'affichage. Ceci est dû au fait que les actions sur les objets swing doivent se produire à partir du thread d'événement pour maintenir la sécurité du thread.

4voto

Selma Points 43

@coobird Tout d'abord, je voudrais vous remercier pour cette réponse détaillée mais malheureusement je ne comprends pas votre citation :

L'exécution de tâches de longue durée sur l'EDT (Event Dispatch Thread) peut provoquer le blocage de l'interface graphique. C'est pourquoi nous avons utilisé SwingUtilities.invokeLater et invokeAndWait pour exécuter des tâches en dehors de l'EDT afin que l'interface graphique reste réactive.

Selon mon humble connaissance :

  • SwingUtilities.invokeLater et invokeAndWait ont permis d'insérer notre code dans l'EDT, Est-ce que c'est juste ? si ce n'est pas le cas, comment faire ?
  • C'est SwingWorker qui a effectué des tâches en dehors de l'EDT afin de maintenir la réactivité de l'interface graphique, et non SwingUtilities.

Merci beaucoup pour toute clarification ^^

P.S : Désolé, je n'ai pas trouvé comment commenter votre réponse au lieu de poster une nouvelle réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X