问题
I have a main class Gui
that handles my GUI and calls a few SwingWorkers (calls to DB and other data sources) that have been put in their own class TablesDataManager
. My issue is that when one of those swingworkers throws an exception within its done90 method (i.e. on the EDT), I want to be able to catch it somehow in the main class and act accordingly (call showErrorAndExit
in my SSCCE below).
Any ideas would be welcome.
Gui.java
import java.awt.event.WindowEvent;
import javax.swing.*;
public class Gui extends JFrame {
private final JLabel waitLabel = new JLabel();
private final JPanel panel = new JPanel();
private final TablesDataManager tblData = new TablesDataManager();
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
private static void createAndShowGui() {
Gui frame = new Gui("My Great GUI");
frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public Gui(String title) {
super(title);
panel.add(waitLabel);
setContentPane(panel);
try {
initData();
} catch (Exception e) {
showErrorAndExit(e);
}
}
//I WANT TO CALL THIS METHOD IF done() THROWS AN EXCEPTION
private void showErrorAndExit(Exception e) {
JOptionPane.showMessageDialog(this,
"An unexpected error occured while initialising the tables. The application will close.\n"
+ (e.getMessage() == null ? "" : e.getMessage()),
"Unrecoverable error",
JOptionPane.ERROR_MESSAGE);
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
private void initData() {
waitLabel.setText("Loading Data");
tblData.initData(new Runnable() {
public void run() {
initTables();
}
});
}
private void initTables() {
waitLabel.setText("Loading Tables");
tblData.initTables(new Runnable() {
public void run() {
finishComponentsSetup();
}
});
}
private void finishComponentsSetup() {
waitLabel.setText("We are done");
}
}
TablesDataManager.java
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
class TablesDataManager {
private final InitData initData = new InitData();
private final InitTables initTables = new InitTables();
void initData(final Runnable runAfterInit) {
launchWorker(initData, runAfterInit);
}
void initTables(final Runnable runAfterInit) {
launchWorker(initTables, runAfterInit);
}
private void launchWorker(final SimpleSwingWorker worker, final Runnable runAfterWorkerDone) {
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("progress")) {
if (worker.getProgress() == 100) { //update finished
runAfterWorkerDone.run();
}
}
}
});
worker.execute();
}
private class InitData extends SimpleSwingWorker {
@Override
protected Void doInBackground() throws Exception {
//do something in the background
//unfortunately there is a connection problem
throw new IOException("Can't connect to database...");
}
}
private class InitTables extends SimpleSwingWorker {
@Override
protected Void doInBackground() throws Exception {
//do something else in the background
return null;
}
}
private abstract class SimpleSwingWorker extends SwingWorker<Void, Void> {
@Override
protected abstract Void doInBackground() throws Exception;
@Override
public void done() {
try {
get();
setProgress(100);
} catch (ExecutionException | InterruptedException e) {
System.out.println("Got that?");
//WHAT DO I DO WITH IT???
throw new RuntimeException(e);
}
}
}
}
回答1:
Keep a reference to your workers in your GUI and in your workes add a try catch. In the catch assign the exception to a variable and add a getter to it. IN your GUI, when your worker is done, just check if there is an exception in the worker or not.
回答2:
An alternative (with slightly lower coupling) I'm nowadays playing with is to let the worker fire a propertyChange with the exception as newValue
@Override
protected void done() {
try {
get();
} catch (Exception e) {
firePropertyChange("done-exception", null, e);
}
}
来源:https://stackoverflow.com/questions/9502149/catch-exception-thrown-by-a-swingworker-located-in-a-different-class