简体   繁体   中英

How to call SwingWorker .get() without freezing GUI?

I made a sample to make the question clear :

public class Worker extends SwingWorker<Integer, Integer> {
    private GeneralUserInterface gui;

    public Worker(GeneralUserInterface gui){
        this.gui = gui;
    }

    @Override
    protected Integer doInBackground() throws Exception {
        int someResultToReturn = 10;

        for(int i=0; i<100; i++){
            Thread.sleep(50);//The Work
            publish(i+1);//calls process, which updates GUI
        }

        return someResultToReturn;
    }

    @Override
    protected void process(List<Integer> values) {
        for (Integer val : values) {
            gui.updateProgressBar(val);
        }
    }
}

private void jButtonDoWorkActionPerformed(java.awt.event.ActionEvent evt) {
    Worker worker = new Worker(this);
    worker.execute();

    try {
        int resultToGet = worker.get();//Obviously freezes the GUI
    } catch (InterruptedException | ExecutionException ex) {}

    //NEXT LINE NEEDS THE RESULT TO CONTINUE
}

public void updateProgressBar(int value){
    this.jProgressBar1.setValue(value);
}

As you would guess, the call to worker.get() makes the UI unresponsive, which is normal since it waits the thread to finish. How is this kind of problem usually solved ?

How is this kind of problem usually solved ?

Normally what you do is override the Swingworker.done() method. Done is executed in the GUI thread when your background thread is completed. Then you can safely call get without blocking and do whatever you need to do.

Here is one way to do this:

public class Worker extends SwingWorker<Integer, Integer> {
    private GeneralUserInterface gui;

    // omitted...

    @Override
    protected Integer doInBackground() throws Exception {
        int someResultToReturn = 10;

        for(int i=0; i<100; i++){
            Thread.sleep(50);//The Work
            publish(i+1);//calls process, which updates GUI
        }

        return someResultToReturn;
    }

    // omitted...

    @Override
    protected void done() {

        try {
           int resultToGet = worker.get();//Obviously freezes the GUI
        } catch (InterruptedException | ExecutionException ex) {}

       //NEXT LINE NEEDS THE RESULT TO CONTINUE
   }
}

private void jButtonDoWorkActionPerformed(java.awt.event.ActionEvent evt) {
    Worker worker = new Worker(this);
    worker.execute();
}

However this may not be the most convenient design. I find it works best to have the GUI stuff be the public class, then create the swing worker as a non-static inner class. That way the "done" method has easy access to all the GUI private variables.

a better solution is to provide your SwingWorker with an instance of an interface implemented by your GUI. That way you can have your Worker update the GUI.

public class GUI implements GUI_INTERFACE{
....
new foo(this);

@Override
public void INTERFACE_METHOD(Object info){
//Update Gui variables here.
}

}

For your SwingWorker:

class foo extends SwingWorker{
GUI_INTERFACE int
public foo(GUI_INTERFACE bar){
int=bar
}


}

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