Basically, you need to have your infinite loop run in another Thread than the EDT. And whenever you want to update your GUI, do it on the EDT, using a SwingUtilities.invokeLater
call. The delay for calling the update of the GUI in invokeLater
will be barely noticeable. SwingUtilities.invokeLater
is not based on a polling mechanism. The only thing it does is transform a Runnable
into an event which is then posted on the EDT. The EDT will then execute your Runnable
as soon as possible, so most of the time, instantly.
Now for the pattern on how to communicate between your Thread and your GUI, you can simply use the "Observer" pattern. Your voice recognition thread is somehow a model and your UI simply listens for changes on that model. Whenever the model changes, the UI updates itself.
I made a dummy example of such thing. For the "Observer" pattern, I used the PropertyChangeSupport
for it.
For the model, I created a dummy thread which generates random "command" every once in a while and the UI updates itself accordingly:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class TestThreadingAndGUI implements PropertyChangeListener {
private JFrame frame;
private JLabel label;
private DummyRunnable runnable;
public static class DummyRunnable implements Runnable {
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private String command;
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
@Override
public void run() {
Random random = new Random();
while (true) {
try {
Thread.sleep(((random.nextInt(3)) + 1) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 15; i++) {
sb.append((char) ('a' + random.nextInt(26)));
}
setCommand(sb.toString());
}
}
public String getCommand() {
return command;
}
private void setCommand(String command) {
String old = this.command;
this.command = command;
pcs.firePropertyChange("command", old, command);
}
}
protected void initUI(DummyRunnable runnable) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
frame.add(label);
frame.setSize(600, 600);
frame.setVisible(true);
this.runnable = runnable;
runnable.addPropertyChangeListener(this);
}
private void executeCommand() {
label.setText(runnable.getCommand());
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("command")) {
// Received new command (outside EDT)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Updating GUI inside EDT
executeCommand();
}
});
}
}
public static void main(String[] args) {
final DummyRunnable runnable = new DummyRunnable();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TestThreadingAndGUI testThreadingAndGUI = new TestThreadingAndGUI();
testThreadingAndGUI.initUI(runnable);
}
});
new Thread(runnable).start();
}
}