C graphics
C graphics

Reputation: 7458

java - How to use invokeLater to synchronize some IO with UI

In my java application I am using swing to implement the UI. There is a button called theButton which is engaged with some IO operation in the following timely steps :

  1. the button originally has the text "Click to connect"
  2. then before the connect operation starts I want the theButton reads "Connecting in progress..."
  3. then the IO operation gets started
  4. once the IO operation is done theButton now reads "connected ( click to disconnect)".

    • Issue:
    • I am using the following code, but first of all the button's text doesn't change to "Connecting in progress..." before the IO starts! as well button doenst actually get disabled before the IO starts! What should I do here?

--

// theButton with text "Click to connect is clicked"
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);// to avoid clicking several times! Some users cannot wait
theButton.repaint();
// doing some IO operation which takes few seconds
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
theButton.repaint();
}
});

Upvotes: 0

Views: 478

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Your problem is here:

javax.swing.SwingUtilities.invokeLater(new Runnable() {
  public void run() {
    theButton.setText("Trying to connect...");
    theButton.setEnabled(false);
    theButton.repaint();

    // doing some IO operation which takes few seconds // **********

    theButton.setText("connected ( click to disconnect)");
    theButton.setEnabled(true);
    theButton.repaint();
  }
});
  • The code marked with the ******* comment is running on the EDT and will tie it up freezing your app and all it's painting.
  • Use a SwingWorker instead to run the code in a background thread.
  • Note that there is no need to use invokeLater(...) for code in an ActionListener since this code is already running on the EDT by default.
  • Also get rid of your repaint() calls since they aren't needed and they don't help.
  • Add a PropertyChangeListener to your SwingWorker to listen for when it is done, and then you can reset your JButton.

Instead do:

// code not compiled nor tested
javax.swing.SwingUtilities.invokeLater(new Runnable() {
  public void run() {
    theButton.setText("Trying to connect...");
    theButton.setEnabled(false);

    MySwingWorker mySwingWorker = new MySwingWorker();

    mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
      // listen for when SwingWorker's state is done
      // and reset your button.
      public void propertyChange(PropertyChangeEvent pcEvt) {
        if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
          theButton.setText("connected ( click to disconnect)");
          theButton.setEnabled(true);
        }
      }
    });

    mySwingWorker.execute();
  }
});

and

// code not compiled nor tested
public class MySwingWorker extends SwingWorker<Void, Void> {
  @Override
  public void doInBackground() throws Exception {
    // doing some IO operation which takes few seconds
    return null;
  }
}

And be sure to read: Concurrency in Swing.

Upvotes: 3

Related Questions