D. Sikilai
D. Sikilai

Reputation: 497

EventQueue.invokeLater() has some bugs

When this code is run everything works fine, but problem comes in main when I change the code inside to be used with EventQueue.invokeLater()

package dav.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JProgressBar;

public class SplashWindow extends JFrame {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JPanel contentPane;
    private JProgressBar progressBar;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        //from here...
        final SplashWindow frame = new SplashWindow();                  frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        for (int i = 0; i < 100; i++) {
            frame.setProgress(i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
//to here...
    }

    /**
     * Create the frame.
     */
    public SplashWindow() {
        setUndecorated(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        JPanel panel = new JPanel();
        contentPane.add(panel, BorderLayout.CENTER);

        progressBar = new JProgressBar();
        progressBar.setMaximum(100);
        progressBar.setValue(0);
        progressBar.setForeground(Color.CYAN);

        panel.setBackground(Color.BLUE);
        GroupLayout gl_panel = new GroupLayout(panel);
        gl_panel.setHorizontalGroup(gl_panel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel.createSequentialGroup().addContainerGap()

                        .addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 420, Short.MAX_VALUE).addContainerGap()));
        gl_panel.setVerticalGroup(gl_panel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel
                        .createSequentialGroup().addGap(245).addComponent(progressBar, GroupLayout.PREFERRED_SIZE,
                                GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                        .addContainerGap(31, Short.MAX_VALUE)));
        panel.setLayout(gl_panel);

    }

    public void setProgress(int i) {
        progressBar.setValue(i);
        if (i+1>=progressBar.getMaximum()){
            dispose();}
    }
}

changing the main method to

public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
final SplashWindow frame = new SplashWindow();                  frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                    for (int i = 0; i < 100; i++) {
                        frame.setProgress(i);
                        try {
                            Thread.sleep(100);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

No exception but it doesn't work as expected and as you all know using EventQueue is preferable than working inside a main method due to EDT.

But the following works.

final SplashWindow frame = new SplashWindow();
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        for (int i = 0; i < 100; i++) {
            frame.setProgress(i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

Though progress bar gets null at some point and oops! NullPointerException is thrown. As you can see am making a custom SplashWindow instead of the built in one. Need help and please don't point out some libraries.

Upvotes: 1

Views: 307

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347332

There's nothing wrong with EventQueue.invokeLater, it's all in how you are using it.

This...

EventQueue.invokeLater(new Runnable() {
    public void run() {
        //...
    }
});

is saying, at some point in the future, do this. BUT, you immediately follow it up with...

for (int i = 0; i < 100; i++) {
    frame.setProgress(i);
    try {
        Thread.sleep(100);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

which is probably going to execute BEFORE invokeLater has had a chance to run.

A overall better solution would be to use SwingWorker instead and start it within the EventQueue.invokeLater, once you have ensured that the state of the UI has been established.

Remember, Swing is not only single threaded, it is NOT thread safe. This means that not only should you not block the main UI thread (ie the Event Dispatching Thread), should also not update the UI, or anything it relies on, from outside of it

See Worker Threads and SwingWorker for more details

Upvotes: 3

Related Questions