jeudi 11 octobre 2012

SwingWorker et mise à jour UI

Lorsque l'on souhaite utiliser SwingWorker afin d'effectuer un traitement mais sans bloquer son interface graphique, on peut être confronter au problème de la communication de son thread de traitement(SwingWorker) vers l'interface graphique.

J'explique :
Ci-dessous notre classe qui étends de SwingWorker
public class MySwingWork extends SwingWorker<Integer, String>
{
    private final Informable informable;

     public MySwingWork (Informable informable)
    {
        this.informable = informable;
    }
    @Override
    protected Integer doInBackground() throws Exception
    {
        /*     WORK   */
        //C'est cette méthode qui mettra à jour le textArea
        publish("test");
        return null;
    }

    @Override
    protected void process(List<String> strings)
    {
        for (String s : strings)
            informable.messageChanged(s);
    }
}

Notre classe avec l'interface graphique :
public class monIHM extends JFrame
{
       public void init()
       {
            /*Initialisation de l'interface graphique*/
            //Text Area qui sera mis à jour pendant le traitement du SwingWorker
            JTextArea textArea=new JTextArea();

            //Interface qui permettra de mettre à jour l'UI en direct avec le swingWorker
            Informable informable = new Informable()
            {
              @Override
              public void messageChanged(String message)
              {
                  final String s = message;
                  Display.getDefault().syncExec(new Runnable()
                  {
                      public void run()
                     {
                        //Modification du TextArea
                        textArea.setText(s);
                    }
                });

            }
           };

           MySwingWork mySwingWork=new MySwingWork(interface);
          
           SwingUtilities.invokeLater(new Runnable()
            {
                  public void run()
                   {
                          /*  Démarrage du SwingWorker. */
                                mySwingWork.execute();
                            }
                     });       
        }
}

Et l'interface qui permet de mettra à jour le composant graphique :
public interface Informable
{
    void messageChanged(String message);
}



C'est la méthode "publish" qui se trouve dans le "doInBackGround" qui permet de mettre à jour le composant graphique.
A noter que dans la méthode "messageChanged" de l'interface "Informable", nous somme obliger d'utiliser ceci :
Display.getDefault().syncExec(new Runnable()
                  {
                      public void run()
                     {
                        //Modification du TextArea
                        textArea.setText(s);
                    }

Si l'on utilise pas ceci, on obtient une erreur AWT-EventQueue-0 thread Invalid thread access, puisque le thread du SwingWorker va essayer d'aller modifier un composant graphique qui est lui-même géré par un autre Thread dont il n'aura pas accès (lien expliquant ce comportement http://www.eclipse.org/swt/faq.php#uithread )