Search code examples
javaswingjdialoginvokelater

JDialog lags after closing


In my code, I initialize a JDialog:

dialog = new JDialog( frame, "Login", true );
dialog.setContentPane( panel );
dialog.setDefaultCloseOperation( JDialog.HIDE_ON_CLOSE );
dialog.setBounds( new Rectangle( 50, 50, 500, 500 ) );

When a button in my main application is clicked, I show the dialog and then run an expensive method with the data I get from it:

dialogWrapper.show(); // Runs dialog.setVisible( true ) directly
LoginCredentials credentials = dialogWrapper.getCredentials(); // Gets data from dialog
try {
    SwingUtilities.invokeLater( new Runnable() {
        @Override
        public void run() {
            progressBar.setIndeterminate( true );
            mainWindow.getFrame().repaint();
            accountModel.login( credentials );
            System.out.println( "Successful login." );
            mainWindow.getFrame().revalidate();
            mainWindow.getFrame().repaint();
            progressBar.setIndeterminate( false );
        }
    } );
} catch ( Exception ex ) {
    // ...
}

My problem is that as soon as I click the button that runs dialog.setVisible( false ):

  • The dialog disappears
  • The frame completely freezes up (the progress bar state doesn't ever change)
  • After the message "Successful login." appears on the console, the frame is still frozen up
  • After around 10 seconds have passed, the frame finally repaints and all the status messages I call as part of accountModel.login() appear on it

How can I make my main window components responsive as the login code is running?

As you can see, I have the entire thing wrapped in a SwingUtilities.invokeLater() call, but that doesn't seem to be helping at all.


Solution

  • As you can see, I have the entire thing wrapped in a SwingUtilities.invokeLater() call,

    That is the problem. The invokeLater() places code on the EDT which means the GUI can't repaint itself of respond to events until the long running task is finished executing.

    So the solution is to use a separate Thread for the long running task, then in the Thread when you need to update the GUI you use the invokeLater().

    Or, you can also use a SwingWorker which will create the Thread for you and then you can add code to the done() method of the SwingWorker to update the GUI.

    Read the section from the Swing tutorial on Concurrency for more information about the Event Dispatch Thread (EDT) and the SwingWorker.