Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Java splash screen "freezes" until the whole application has loaded

I have a program, that takes a long time to load. Because of this, I wanted to develop a splash screen that can provide feedback to the user on what is being loaded. A simple JFrame with an image, label and JProgressBar.

I have been experimenting and the best results I've had are doing this in my main():

SwingUtilities.invokeAndWait(new Runnable() {

    public void run() {

        new SplashScreen();
    }
});

SwingUtilities.invokeAndWait(new Runnable() {

    public void run() {

        //Code to start system
        new MainFrame();
        //.. etc
    }
});

Both SplashScreen and MainFrame are classes extending JFrame. I am also using Substance as a Library.

SplashScreen's constructor adds a JLabel and JProgressBar to itself, packs and sets visible. The JProgressBar is setIndeterminate(true);

When I run my program, my SplashScreen is displayed but the ProgressBar is locked, it doesn't move, not until the rest of the program has started does it start moving as expected.

What am I missing here? All searching I've done doesn't seem to mention this problem and most "custom splash screen" implementations go about it a very similar way to myself.

like image 413
Andy Smith Avatar asked Mar 01 '10 18:03

Andy Smith


4 Answers

Just to attempt to be a little more general than Allain's correct answer.

There is one thread in your system that can (legally) update GUIs.

If you are using that thread to initialize your system, guis will NOT update.

If you are trying to update your GUI from your main thread, nothing will go right.

Always be aware of which thread your GUI is on and be sure not to be doing work on that thread.

Think of GUI updates in this way:

  • your non-gui thread updates a value you are displaying (say the percent done).
  • it posts an update via invokeLater, then continues with other work.
  • The GUI thread executes your invokeLater which updates your GUI with the data passed from the non-GUI thread
  • the GUI thread it exits! This allows other unrelated parts of the system to update the GUI when they need to.

Non-GUI threads are most often threads from Thread.start and the one passed to "main"

The GUI thread is the one executing your "invokeLater" and anything passed to you from a GUI event (button pressed, GUI timer, any Swing listeners.)

Because of this, most often you are already on the GUI thread when it's time to update the GUI--just watch out for startup sequencing and places where threads want to update the display.

like image 109
Bill K Avatar answered Oct 10 '22 13:10

Bill K


invokeAndWait will freeze the Swing thread until the operation it is given completes. So, your progress bar will be 100% forzen until the MainFrame is created.

You should spawn a non-swing thread that updates the Splash Screen using SwingUtilities.invoke(Later|AndWait).

new Thread(new Runnable() { public void run() {
  // Create MainFrame here
  SwingUtilities.invokeLater(new Runnable() {
      public void run() {
         updateProgressHere();
      }
  });

  // Do other initialization
  SwingUtilities.invokeLater(new Runnable() {
      public void run() {
         updateProgressHere();
      }
  });
}).start(); 
like image 25
Allain Lalonde Avatar answered Oct 10 '22 15:10

Allain Lalonde


Other answers have covered most of this but in brief your problem is that you are running the "Code to start system" in the Swing event dispatch thread. All GUI-related code (including component creation) must be run on the EDT but all other code should not be run on the EDT. Try changing your program to do this:

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        new SplashScreen();
    }
});
// Code to start system (nothing that touches the GUI)
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        new MainFrame();
    }
});
//.. etc
like image 43
Russ Hayward Avatar answered Oct 10 '22 15:10

Russ Hayward


Others have mentiones why your progress bar is blocked, I will suggest an alternative solution Java6 has splash screen handling built in to the javaw and java launchers...

The splash screen is displayed even before the JVM and the application's classes are loaded, and then once started the application can update it to reflect its internal loading state, and close it when ready.

Alternatively, for windows-only solutions, the Winrun4J java launcher also has a splash screen feature.

like image 1
RedPandaCurios Avatar answered Oct 10 '22 15:10

RedPandaCurios