简体   繁体   中英

How to deliver object to a long-running SwingWorker from the EDT?

How can the EDT communicate to an executing SwingWorker ? There a lot of ways for the SwingWorker to communicate information back to the EDT - like publish/process and property changes but no defined way (that I have seen) to communicate in the other direction. Seems like good old Java concurrent inter-thread communication would be the way to go via wait() and notify() . This doesn't work. I'll explain later. I actually got it to work but it uses an ugly polling mechanism. I feel like there should be a better way. Here is the process that I am trying to accomplish:

  1. From the main Swing UI (EDT) a user starts a SwingWorker long-running task (the engine).
  2. At some point the engine needs information from the EDT so it communicates this back to the EDT. this could be done through publish/process update of a visible UI component. Importantly, this step DOES NOT block the EDT because other things are also going on.
  3. The engines blocks waiting for an answer.
  4. At some point the user notices the visual indication and provides the required information via some UI (EDT) functionality - like pressing a Swing button.
  5. The EDT updates an object on the engine. Then "wakes up" the engine.
  6. The engine references the updated object and continues to process.

The problem I have with wait() / notify() is that in step 3 any invocation of wait() in doInBackground() causes the done() method to be immediately fired and the SwingWorker to be terminated. I was able to get the above process to work by using an ugly sleep() loop in doInBackground() :

for (;;)
{
    Thread.sleep(10);

    if (fromEDT != null)
    {
        // Process the update from the EDT
        System.out.println("From EDT: " + fromEDT);
        fromEDT = null;
        break;
    }
}

What this really is that in step 5 the engine wakes itself up and checks for updates from the EDT.

Is this the best way to do this? I kind of doubt it.

The following is an mre demonstrating a SwingWorker paused and waiting for user's input:

import java.awt.*;
import java.util.List;
import javax.swing.*;

public class SwingWorkerWaitDemo {

    public static void creategui(){

        JFrame f = new JFrame("SwingWorker wait Demo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.add(new MainPanel());
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args) {
        creategui();
    }
}

class MainPanel extends JPanel {

    private static final String BLANK = "               ";

    private MyWorker swingWorker;
    private final JLabel output, msg;
    private final JButton start, stop, respond;

    MainPanel() {
        setLayout(new BorderLayout(2, 2));
        start = new JButton("Start");
        start.addActionListener(e->start());
        stop = new JButton("Stop");
        stop.setEnabled(false);
        stop.addActionListener(e->stop());
        JPanel ssPane = new JPanel(new FlowLayout(FlowLayout.CENTER));
        ssPane.add(start); ssPane.add(stop);
        add(ssPane, BorderLayout.PAGE_START);

        output = new JLabel(BLANK);
        JPanel outputPane = new JPanel(new FlowLayout(FlowLayout.CENTER));
        outputPane.add(output);
        add(outputPane, BorderLayout.CENTER);

        msg = new JLabel(BLANK);
        respond = new JButton("Respond");
        respond.addActionListener(e->respond());
        respond.setEnabled(false);
        JPanel responsePane = new JPanel();
        responsePane.add(msg); responsePane.add(respond);
        add(responsePane, BorderLayout.PAGE_END);
    }

    @Override
    public Dimension getPreferredSize(){
        return new Dimension(400, 200);
    }

    private void start() {
        start.setEnabled(false);
        stop.setEnabled(true);
        swingWorker = new MyWorker();
        swingWorker.execute();
    }

    private void stop() {
        stop.setEnabled(false);
        swingWorker.setStop(true);
    }

    private void message(String s){
        msg.setText(s);
    }

    private void clearMessage(){
        msg.setText(BLANK);
    }

    private void askForUserResponse(){
        respond.setEnabled(true);
        message("Please respond " );
    }

    private void respond(){
        clearMessage();
        respond.setEnabled(false);
        swingWorker.setPause(false);
    }

    class MyWorker extends SwingWorker<Integer, Integer> {

        private boolean stop = false;
        private volatile boolean pause = false;

        @Override
        protected Integer doInBackground() throws Exception {

            int counter = 0;

            while(! stop){
                publish(counter++);
                if(counter%10 == 0) {
                    pause = true;
                    askForUserResponse();
                    while(pause){   /*wait*/ }
                }
                Thread.sleep(500);
            }
            return counter;
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (int i : chunks) {
                output.setText(String.valueOf(i));
            }
        }

        @Override
        protected void done() {
            message("All done");
        }

        void setStop(boolean stop) {
            this.stop = stop;
        }

        void setPause(boolean pause) {
            this.pause = pause;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM