Изящная обработка исключений в Swing Worker
Я использую потоки в приложении через класс Swing Worker. Он отлично работает, но у меня плохое представление об отображении диалогового окна сообщения об ошибке в блоке try-catch. Может ли это потенциально заблокировать приложение? Это то, что сейчас выглядит:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
public Void doInBackground() {
try {
DoFancyStuff();
} catch (Exception e) {
e.printStackTrace();
String msg = String.format("Unexpected problem: %s", e
.toString());
//TODO: executed in background thread and should be executed in EDT?
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE,
errorIcon);
}//END: try-catch
return null;
}
// Executed in event dispatch thread
public void done() {
System.out.println("Done");
}
};
Можно ли это сделать безопасным способом с использованием среды Swing Worker? Является ли переопределяющим метод publish() хорошим примером здесь?
EDIT:
Было ли это так:
} catch (final Exception e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
e.printStackTrace();
String msg = String.format(
"Unexpected problem: %s", e.toString());
JOptionPane.showMessageDialog(Utils
.getActiveFrame(), msg, "Error",
JOptionPane.ERROR_MESSAGE, errorIcon);
}
});
}
Вызов метода get in done приведет к двум блокам try-catch, поскольку вычислительная часть генерирует исключения, поэтому я думаю, что в конце это чище.
Ответы
Ответ 1
Один из вариантов заключается в использовании SwingUtilities.invokeLater(...)
, чтобы опубликовать действие в EDT
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
JOptionPane.showMessageDialog(
Utils.getActiveFrame(),
msg,
"Error",
JOptionPane.ERROR_MESSAGE,
errorIcon);
}
});
И как вы отметили, SwingWorker
способен сообщать промежуточные результаты, но вам нужно переопределить process(...)
, который вызывается при вызове publish(...)
.
Независимо от того, почему бы не просто установить флаг, если возникло исключение, и если этот флаг установлен, отобразите диалог в done()
, так как он безопасно выполняется в EDT
?
Ответ 2
Правильный способ сделать это:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
protected Void doInBackground() throws Exception {
DoFancyStuff();
return null;
}
// Executed in EDT
protected void done() {
try {
System.out.println("Done");
get();
} catch (ExecutionException e) {
e.getCause().printStackTrace();
String msg = String.format("Unexpected problem: %s",
e.getCause().toString());
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE, errorIcon);
} catch (InterruptedException e) {
// Process e here
}
}
}
Вы не должны пытаться перехватывать исключения в фоновом потоке, а скорее пропускать их непосредственно в SwingWorker, а затем вы можете получить их в методе done()
, вызвав get()
, который обычно возвращает результат doInBackground()
(Void
в вашей ситуации). Если исключение было выбрано в фоновом потоке, то get()
будет его бросать, завернутый внутри ExecutionException
.
Также обратите внимание, что переопределенные методы SwingWorker
protected
, и вам не нужно делать их public
.
Ответ 3
Вы правы, вы нарушаете основное правило Swing, которое не изменяет GUI нигде, кроме как в потоке отправки событий.
Если бы это был я, я бы выбрал событие, которое GUI слушает, чтобы показать сообщение об ошибке. Или вы можете просто обернуть вызов SwingWorker в try catch и показать диалог там.