To speed up application startup I moved some of the GUI generating code to worker thread. That thread creates all the GUI instances and then uses SwingUtilities.invokeLater
to append them to the already running application GUI.
I extended ArrayList
to create a helper class that remembers what should be appended to what.
I use it like this:
// This code runs in some other thread, not Swing thread
GUIAppendList list = new GUIAppendList();
JToggleButton button = new JToggleButton();
// The first variable represents already appended and rendered
// GUI element - for example menu
list.add(menuBarGlobalVariable, button);
// This will create all elements in Swing GUI thread
list.create();
The GUIAppendList creates the items like this:
synchronized public void create() {
if(done)
return;
done = true;
SwingUtilities.invokeLater(new Runner(this));
}
protected static class Runner implements Runnable {
public final GUIAppendList list;
public Runner(GUIAppendList list) {
this.list = list;
}
@Override
public void run() {
for(AddChild c : list) {
c.join();
}
if(!list.after.isEmpty()) {
for(Runnable c : list.after) {
c.run();
}
}
}
}
But since I did that, my application started randomly (not always and never in debug mode so far) freezing. Is it possible that creating GUI elements causes that even if they are not connected to the main Window?
Here's the full appender class:
import java.awt.Component;
import java.awt.Container;
import java.util.ArrayList;
import javax.swing.SwingUtilities;
/**
* This class allows you to prepare a list of swing elements that should be
* joined together. After you create the list, use .create() method which will
* then add all the items within the Swing main thread. This is done using
* invokeLater.
* @author Jakub
*/
public class GUIAppendList extends ArrayList<GUIAppendList.AddChild> {
/**
* Represents two nodes, container and component.
*/
public static class AddChild {
public AddChild(Container parent, Component child) {
this.parent = parent;
this.child = child;
}
final Container parent;
final Component child;
// Joins the two elements together
// THIS MUST BE CALLED IN THE SWING THREAD!!!
void join() {
parent.add(child);
};
}
// Prevents from calling the add in the list multiple times
private boolean done = false;
// List of other actions to be executed after creating the GUI
protected ArrayList<Runnable> after = new ArrayList();
synchronized public void add(Container parent, Component child) {
this.add(new AddChild(parent, child));
}
synchronized public void after(Runnable action) {
after.add(action);
}
/**
* Will ask Swing to run our runnable object which will append all the items.
*/
synchronized public void create() {
if(done)
return;
done = true;
SwingUtilities.invokeLater(new Runner(this));
}
protected static class Runner implements Runnable {
public final GUIAppendList list;
public Runner(GUIAppendList list) {
this.list = list;
}
@Override
public void run() {
// Append all nodes to each other
for(AddChild c : list) {
c.join();
}
// Run after actions
if(!list.after.isEmpty()) {
for(Runnable c : list.after) {
c.run();
}
}
}
}
}