Initializing Java Swing in the background - java

I have a question regarding quick initialization of swing components. At the start of my swing application I have a window that pops up with buttons that allow the user to do a variety of things. Is there I way that I can quickly launch that first window and load the rest of the UI (such as other frames and dialogs) in the background so that there is isn't that initial delay.
Thanks,

Is Swing thread safe?

Yes. Ish. You could do something similar to:
public static void main(String [] args) {
// Construct main Frame on Swing EDT thread
Runnable goVisible = new Runnable() {
public void run() {
JFrame mainFrame = new JFrame();
mainFrame.setVisible();
// etc.
}
};
SwingUtilities.invokeLater(goVisible);
// now the background init stuff
Class.forName("com.yourcompany.view.Dialog1");
Class.forName("com.yourcompany.view.WizardGUI");
Class.forName("com.yourcompany.view.SecondaryFrame");
// Here all the views are loaded and initialized
}

Display blank main frame first and then load the rest of UI. You can use Swing Application Framework (or BSAF now) to init components and build layout when app is ready (main frame is visible) - Application.ready() method. Use http://code.google.com/p/jbusycomponent/ to show that app is loading...

There really is no good solution to this, it is one of the drawbacks of Java. That being said keep reading for my idea.
There are two parts to loading a class.
The JVM loads the class file into
the ClassLoader when it is needed.
The JIT compiles and optimizes the
code the first time the path is run.
You can do what rekin suggests, which is to eagerly load the UI classes before they are needed. That will only partially solve your problem, because you are only getting some of the classes. This will also have the disadvantage of taking up a lot more memory and even the classes in the class loader will be garbage collected if needed.
In order to avoid some of the hassles you are getting with the Reflection Approach.
One method you could try is in your windows make sure the constructor does not display a window, instead have another method that would display the window called init(), Then have a separate Thread from main call create a new on each of the Windows you want to preload.
Do not save the reference to the window.
In the real code you would call the constructor and then init() for each window you wanted to display. This would give you the best possible scenario as far as performance, because now you are loading the classes as well as running the constructor code. Of course the size of the program in memory will be bloated.
public static void main(String [] args) {
// Construct main Frame on Swing EDT thread
Thread thread = new Thread() {
public void run() {
// now the background init stuff
new com.yourcompany.view.Dialog1();
new com.yourcompany.view.WizardGUI();
new com.yourcompany.view.SecondaryFrame();
// Here all the views are loaded and initialized
}
};
JFrame mainFrame = new JFrame();
mainFrame.setVisible();
// etc.
}

That's the goeal of having a Splash Screen(with or without a progress bar - much nicer with it, of course). You should show a nice splash to your users and then you initialize all your components starting with the main window on the EDT thread and at the end you show up your frame. Creating Swing components outside EDT might(it will sure do) create problems with visibility at least but also with concurrent access between your thread and EDT. DON'T do that, it's hard to detect these issues and it might manifest random on different hardware.
Of course, if you have a progress bar you need some free EDT time to render progress bar changes - actually even to refresh the splash screen itself(repainting the background image if another application blocked for a while your splash) you need free time on the EDT.
You should split your initializations in smaller blocks that will not take more than 500ms to run and you schedule them on the EDT with SwingUtilities.invokeLater.

Related

Should the first JFrame be created with 'invokeLater' or can it be created directly from main?

I've recently started learning how to use Swing and graphics in Java and have come across two different approaches for designing a GUI.
1) To have the program's main method in an instatiation of the JFrame class.
2) To have a class which calls SwingUtilities.invokeLater() at the end of the main method to delay the thread that deals with graphics until after the initialisation of the main method.
e.g. 1.
class program extends JFrame {
public static void main(String[] args) {....}
}
e.g. 2.
class program implements Runnable {
public static void main() {
program p = new program();
SwingUtilities.invokeLater(p);
}
public void run() { ... }
}
How important is it to make the program thread safe as in approach 2? Also, what are the advantages and disadvantages of each approach? i.e. When would you use one approach over the other? etc.
I have run into simple Swing applications that fail outright if not started on the Swing event thread, this happened first for me when setting look and feel. Also correct running is not guaranteed in any situation unless the GUI is started on the Swing event thread, and so for general Swing advice, I recommend
Always try to start your GUI on the Swing event thread.
Avoid extending JFrame as it's almost never needed, and limits your code.
Do extend JPanel if your GUI does graphics, especially if it does animation
Or if you want to have more control over setting the size of your GUI, and override getPreferredSize() for this.
You can paint yourself in a corner by having your class extend JFrame, forcing you to create and display JFrames, when often more flexibility is called for. More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This will greatly increase the flexibility of your GUI coding. You also never will want to draw directly in a JFrame as this risks mis-drawing one of its critical components but do often draw in JPanels, especially if doing animation, and for this you will need to extend JPanel.

InvokeLater() - will just once be enough?

I use the recommended code to start my interactive program, which uses Swing. :
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(
new Runnable() {
public void run() {
createAndShowGUI();
} } ) };
It creates a JFrame, call it "Foo", and ends when the user closes that window (by using the OS' Close- Window X icon or another OS way to close the app)
I want to display another window, "Bar", get user input, close that window with my own Swing calls, and then display the "Foo" window.
I could reuse the same JFrame for Foo and Bar, and just clear it out in-between. But I prefer to make each of them more independent in their design. So, an ActionListener in Foo would need to close Foo's frame, and call the code that displays Bar.
Do I need to use InvokeLater() to call the code that displays Bar? To get more design independence, should my main() be starting and synchronizing two threads?
User triggered action listener code is executed in the EDT, so you do not need to wrap it again. That said, it may well be that using CardLayout would be more appropriate than multiple frames.
I could reuse the same JFrame for Foo and Bar, and just clear it out
in-between. But I prefer to make each of them more independent in
their design. So, an ActionListener in Foo would need to close Foo's
frame, and call the code that displays Bar.
Do I need to use InvokeLater() to call the code that displays Bar? To
get more design independence, I am unclear on the operation of the
EDT.
you would use pack() and setVisible(true) wrapped into invokeLater in all cases for
for new Top-Level Container - alyways
for container created but never dispayed - always
for container once visible, then hidden and again visible on the screen - always
doesn't matter if is invoked from Swing Listener (by default on EDT) or not
to my point 3rd. to avoiding any unwanted Graphics lacks in the case that you reused Top-Level Container (is specifics, very short moment, but could be visible, but not, never annoying)
old value is visible, then immediatelly refreshed to current
old JComponent is visible, then immediatelly refreshed with current JComponents
relayout/ pack() , the same issue as a.m.
invokeLater to delay (in most casses with success) this event to the end of EDT
see my view translated to the code demonstration about

Splash screen showing progress of GUI being built in Event Dispatch Thread

According to http://www.javaworld.com/javaworld/jw-08-2007/jw-08-swingthreading.html?page=5 all GUI operations should be executed in Event Dispatch Thread.
While I understand the reasons why Swing was designed with single threaded model in mind, I can't figure out how to solve the following problem:
Let's have a method called buildGui() which initializes main GUI of the application. Call to buildGui() method takes 10 seconds to return.
Let's have another method called splashScreen() which shows the JDialog with indeterminate JProgressBar inside. The purpose of the JDialog is obvious: it gives user feedback that application is loading resources, initializing components, etc.
Now, if my program calls:
splashScreen(); // build and show splash screen in EDT
buildGui(); // build main GUI in EDT
the splash screen is freezed for 10 seconds, because it waits for buildGui() to finish.
Do you have any ideas how to show splash screen which depicts the status of GUI initialization (buildGui()) while following Swing's single threaded model?
Use AWT for splash screens, so they can be loaded prior to loading the Swing package. The plug-in's own SplashScreen is pure AWT. See also this question re. use of SplashScreen & the EDT.
While I'm typically saying 'this millennium, use Swing components', it makes a lot of sense to use AWT for a splash.
Take a look at the Swing tutorial about splash screens which contains sample code doing exactly what you want

How to preload Swing components

I know this is sort of a vague question, but I will try to make it as clear as possible. When my Java app launches for the first time, it does some checking to see if files and directories exist and checks for an internet connection.
The app then allows the user to move on to the dashboard, which requires loading and adding many Swing components to the Background Panel. This takes time.
I was wondering how I could make it so that during the loading process at the start, the app loads all of the Swing components Images etc. so that they appear instantly when the user executes the command to do so.
I can load all of the components like this:
JButton = new JButton("blah");
but I'm not sure that's enough to make the components appear instantly, wouldn't adding several image filled Swing components at the same time still lag the UI thread, even if it was already "loaded" as seen above?
Thanks!
on the components use
setVisible(false)
for example
public class myParentPanel extends JPanel{
public myParentPanel{
//add all the child components
//Do what ever takes along time
setVisible(false); //this then makes in invisible but still
//allows all the set up code to run
}
public void showParent(){
setVisible(true);
invalidate();
}
}
and then create a method to make them visible when required. Hence all your setting in the constructors ca be called then when you call your method say:
drawWindow()
it then only has to call setVisible(true) and invalidate the screen to call their painting methods :).
An advancement on this would be to *run your setups i.e your checking and loading the panels methods on a separate threads*so you don't have to wait for a sequential loading. So on your loading you may to use an anonymous class
SwingUtilities.invokeLater(new Runnable(){ public void run(){
//do my display set up
}});

What is the correct way of manipulating Swing components at program startup?

I'm creating an application in Swing using NetBeans. I would like to be able to manipulate some components during its startup (just once), after the window's made visible, for example update a progress bar. To this end, I have the app's main class, called MainWindow:
public class MainWindow extends JFrame
{
public MainWindow()
{
initComponents(); // NetBeans GUI builder-generated function for setting
// up the window components
}
public void Init()
{
loadLabel.setText("Loading....");
loadProgressBar.setValue(20);
doSomething();
loadProgressBar.setValue(40);
doSomething();
loadProgressBar.setValue(80);
doSomething();
loadProgressBar.setValue(100);
loadLabel.setVisible(false);
loadProgressBar.setVisible(false);
}
/* .... */
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
mainHandle = new MainWindow();
mainHandle.setVisible(true);
mainHandle.Init();
}
});
}
}
The problem is that the effect of the statements for updating the progress bar (or manipulating any other GUI component) within the Init() function can't be observed. If the Init() function is called from within main() as shown above, the window appears, but is empty, the Init() function executes and returns, only afterwards the window draws its contents but any changes made by Init() aren't visible because the window was empty and inactive the whole time. I also tried calling init from the windowOpened() AWT event, which executes after the window is fully drawn, but amazingly putting any statements for manipulating components there seems to have no effect, or rather they are put in a queue, and executed rapidly at some point in succession, so only the effect of the last one (hiding of the elements) can be observed. The only way I managed to get it working was to remove the whole invokeLater(new Runnable()...) mantra and put the new MainWindow(), setVisible(), Init() sequence directly in main(), which I guess is very ugly and breaks the concept of the gui running in a threaded manner. What is the right way to do this? Where do I put code to be executed first thing when the gui is ready to be manipulated, execute the statements once and return control to the main event loop?
I guess at the moment this is working in such a way, that while the Init() function is operating, any operations on the gui components are suspended (the drawing thread isn't separate and waits for Init() to finish before the manipulations are executed). Maybe I should make Init() a new thread... only how and what kind?
Thanks.
You could change the EventQueue.invokeLater() to invokeAndWait(), and move the call to init() out to a second EventQueue.invokeLater() call.
If (as looks to be the case) doSomething() takes a noticable amount of time, a better idea is to move the Init code into the body of a SwingWorker. This could be executed from the MainWindow() constructor or after the setVisible() call in main and is the idiomatic way to have a responsive GUI (in case the user gets bored waiting and wants to quit) and display some visible signs of progress.
See the process and publish methods for details on how to update the progress bar between doSomething() calls.
You may also want to look into ProgressMonitors for another alternative that would deal with the dialog box etc for you.
There are several things you can do:
For windows (such as JFrame or JDialog) you can attach WindowListener and do your manipulations in windowOpened method.
Override addNotify method and do your control manipulations there.
Attach HierarchyListener and do your manipulations whenever displayability of component changed.
Always make sure your do your component manipulations on EDT. Use SwingUtilities.invokeLater for simple UI updates or SwingWorker for long running tasks

Categories

Resources