My question is a theory-based question, but it does meet a specific need I have.
What do you do when your SwingWorker throws an Exception that you a) can anticipate and b) need to recover from and continue, but you want to notify the user that this error has happened? How do you grab the expected exception and notify the user without violating the "No Swing code from doInBackground()
" rule?
I have, in consideration of this problem, developed a SSCCE that I would like to put forth with the questions below it.
SSCCE:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String args[]) {
final JFrame frame = new JFrame();
JButton go = new JButton("Go.");
go.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Task(frame);
}
});
frame.add(go);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
static class Task extends SwingWorker<Void, Void> {
JFrame parent;
JDialog dialog;
public Task(JFrame parent) {
this.parent = parent;
dialog = new JDialog(parent);
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(true);
dialog.add(jpb);
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
execute();
}
@Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < 100000; i++) {
System.out.println(i);
try {
if(i == 68456) throw new IllegalStateException("Yikes! i = 68456.");
} catch (final IllegalStateException e) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(parent, "Error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
});
}
}
return null;
}
@Override
protected void done() {
if (!isCancelled()) {
try {
get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("done");
dialog.dispose();
}
}
}
Is it valid to call SwingUtilities.invokeAndWait()
within the doInBackground()
method? I did some Thread profiling on this and the results were thus:
Once the "Go" button is pressed, the SwingWorker-pool-1-thread-1
thread goes Green. THEN, when the if condition is met, the error is thrown, and the error dialog is displayed, the thread goes yellow, and there is indeed a green "blip" on the AWT-EventQueue-0
thread, indicating that the EDT was invoked. I waited about 10 seconds, pressed "ok," and the SwingWorker
thread went green again.
Are there any potential pitfalls to doing something like this? Does anyone have experience with notifying users of computation errors in real time from the SwingWorker
?
I'll be honest, this approach has me leery. It seems unorthodox but I cannot say for certain whether this is a bad idea.
I see no problem with using invokeAndWait()
when the user actually needs to approve. If not, as shown in this example, a SwingWorker<Void, String>
can simply call publish()
with data and error messages interleaved. A suitable message appended in done()
would allow the user to review the accumulated output if necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With