Search code examples
javaswingjpanelswingworker

SwingWorker get() freezing interface


I am using SwingWorker to get some long running database calls done in the background. Right before executing the SwingWorker I launch a jpanel with label to inform user that the app is doing some background work. The problem is that if I use the SwingWorker get() method to fetch the results of the long running operations, my jpanel freezes and nothing gets displayed. If I don't use the get() method, the jpanel appears normally.

Can you please let me know what I may be doing wrong? I already spend hours on this without figuring out why. The SwingWorker gets executed after all jpanel creation is done, I don't understand why it's getting hang.

Thanks a lot!

Function that creates jpanel and starts SwingWorker:

private void snapshotOldVariables() {

    JFrame loadingJFrame = new JFrame();
    LoadingJPanel loadingJPanel = new LoadingJPanel();

    Dimension d1 = new Dimension();
    d1.setSize(500, 500);
    loadingJFrame.setResizable(false);
    loadingJFrame.setMinimumSize(d1);
    loadingJFrame.setTitle("Loading...");
    loadingJFrame.setLocationRelativeTo(null);
    loadingJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    loadingJFrame.add(loadingJPanel);
    loadingJFrame.setVisible(true);

    loadingJPanel.updateStatus("Creating snapshot of values...");

    MySwingWorker worker = new MySwingWorker(tableRows, selectedItems);
    worker.execute();

    GMSnapshotVO snap = null;
    try {
        snap = worker.get();
    } catch (InterruptedException | ExecutionException e) {
        System.out.println(e);
    }

    loadingJPanel.updateStatus("Complete");
    loadingJFrame.dispose();


    productIds = snap.getProductIds();
    oldLocationIds = snap.getOldLocationIds();
    oldStatusIds = snap.getOldStatusIds();

}

Screenshot of jpanel freezed while function waits for worker.get(): enter image description here

Function with commented worker.get() code block: private void snapshotOldVariables() {

    JFrame loadingJFrame = new JFrame();
    LoadingJPanel loadingJPanel = new LoadingJPanel();

    Dimension d1 = new Dimension();
    d1.setSize(500, 500);
    loadingJFrame.setResizable(false);
    loadingJFrame.setMinimumSize(d1);
    loadingJFrame.setTitle("Loading...");
    loadingJFrame.setLocationRelativeTo(null);
    loadingJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    loadingJFrame.add(loadingJPanel);
    loadingJFrame.setVisible(true);

    loadingJPanel.updateStatus("Creating snapshot of values...");

    MySwingWorker worker = new MySwingWorker(tableRows, selectedItems);
    worker.execute();

    /* COMMENT STARTS HERE
    GMSnapshotVO snap = null;
    try {
        snap = worker.get();
    } catch (InterruptedException | ExecutionException e) {
        System.out.println(e);
    }

    loadingJPanel.updateStatus("Complete");
    loadingJFrame.dispose();

    productIds = snap.getProductIds();
    oldLocationIds = snap.getOldLocationIds();
    oldStatusIds = snap.getOldStatusIds();
    COMMENT ENDS HERE */

}

Screenshot of jpanel not freezed and appearing correctly: enter image description here


Solution

  • As far as I know you should not call SwingWorker's get() method directly from UI thread unless you are sure it is done processing. Instead you should call it inside overriden MySwingWorker.done(), where you can be sure that background task finished executing.

    It is not relevant, that all JPanel creation is done before blocking as Swing still needs its main UI thread for repainting and updating UI and to stay responsive. By calling get() you are blocking it.

    From UI thread you should just call execute() and all the processing of results (productIds = snap.getProductIds(); oldLocationIds = snap.getOldLocationIds(); oldStatusIds = snap.getOldStatusIds(); in your case) should be done inside done() callback.

    A simple example is available in this answer.

    In your case it should go something like this:

    //inside MySwingWorker
    @Override
    protected void done() {
        try {
            System.out.println("My long running database process is done. Now I can update UI without blocking/freezing it.");
            GMSnapshotVO snap = get();
            loadingJPanel.updateStatus("Complete");
            loadingJFrame.dispose();
            productIds = snap.getProductIds();
            oldLocationIds = snap.getOldLocationIds();
            oldStatusIds = snap.getOldStatusIds();
            //..do other stuff with ids
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    

    Hope it helps.