Search code examples
javamultithreadingswinguser-interfaceswingworker

Retrieving data from a map when using a SwingWorker


I have a class that creates a GUI and has a JComboBox with an ItemListener. On an ItemEvent.Selected event I create the worker and try to read data into a map, but the contents is always empty.

My guess is that this is happening due to the thread executing and leaving the ItemListener before the thread completes.

I've added relevant pieces of my code and hoping you guys can help me determine where the issue is.

private void updateUI(String text) {

   StringUtilities.invokeLater(new Runnable() {

      @Override 
      public void run() {
          text.append(text + "\n");
      }
   }
}

private class ComboBoxListener extends ItemListener {

@Override
public void itemStateChange(ItemEvent e) {

   if(e.getStateChange() == ItemEvent.DESELECTED) { return; }
   if(e.getStateChange() == ItemEvent.SELECTED) {

       //do stuff
       Map<String, String> map = new HashMap<String, String>();

       new MyWorker(var1, var2) {

          @Override
          public updateUI(String text) {
               updateMainUI(text);
          } 

          @Override
          public updateMap(String name, String value) {
              map.put(name, value);
          }
       }.execute();

       for(Map.Entry<String, String> entry : map.entrySet()) {
          // do something with map contents
       }
     }
   }
}

The updateUI and updateMap are abstract methods in MyWorker that I'm overriding so that I can send updates to the gui.

In short, how can I get the data back from the worker so that I can execute it before the event is done?

I wrote most of this from memory since I don't have the actual code in front of me at the moment, so please ignore minor typos and syntax issues.


Solution

  • A SwingWorker executes its doInBackground() method asynchronously on another thread; the results will become available at indeterminate times in the future, but you're examining the map entries immediately. Instead, publish() each Map.Entry<String, String> as it becomes available in doInBackground() and process() the values as they become available.

    If the intermediate results can be usefully displayed in another component, update that component's model, as shown here for the text of a JLabel. If not, override done() to use the completed result, as shown here for an Icon.