java Swing Worker 中的优雅异常处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6523623/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Graceful exception handling in Swing Worker
提问by fbielejec
I am using threading in application through Swing Worker class. It works fine, yet I have a bad feeling about showing an error message dialog in try-catch block. Can it potentially block the application? This is what it looks right now:
我正在通过 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");
}
};
Can it be done in a safe way using Swing Worker framework? Is overriding publish() method a good lead here?
可以使用 Swing Worker 框架以安全的方式完成吗?在这里重写 publish() 方法是一个很好的引导吗?
EDIT:
编辑:
Did it like this:
是不是这样的:
} 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);
}
});
}
Calling get in done method would result in two try-catch blocks, as the computational part throws exceptions, so I think this is cleaner in the end.
调用 get in done 方法会导致两个 try-catch 块,因为计算部分会抛出异常,所以我认为这最终更清晰。
采纳答案by mre
One option is to use SwingUtilities.invokeLater(...)
to post the action on the EDT
一种选择是用于SwingUtilities.invokeLater(...)
将操作发布到EDT
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
JOptionPane.showMessageDialog(
Utils.getActiveFrame(),
msg,
"Error",
JOptionPane.ERROR_MESSAGE,
errorIcon);
}
});
And as you noted, SwingWorker
is capable of reporting intermediate results, but you'll need to override process(...)
, which is called when you invoke publish(...)
.
正如您所指出的,SwingWorker
能够报告中间结果,但您需要覆盖process(...)
,它在您调用publish(...)
.
Regardless, why not just set a flag if an exception occurs, and if that flag is set, show the dialog in done()
since it's executed safely in the EDT
?
无论如何,为什么不在发生异常时设置一个标志,并且如果设置了该标志,则显示对话框,done()
因为它在EDT
?
回答by jfpoilpret
The right way to do it is as follows:
正确的做法如下:
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
}
}
}
You should NOT try to catch exceptions in the background thread but rather let them pass through to the SwingWorker itself, and then you can get them in the done()
method by calling get()
which normally returns the result of doInBackground()
(Void
in your situation). If an exceptionwas thrown in the background thread then get()
will throw it, wrapped inside an ExecutionException
.
您不应该尝试在后台线程中捕获异常,而是让它们传递给 SwingWorker 本身,然后您可以done()
通过调用在方法中获取它们,该方法get()
通常返回doInBackground()
(Void
在您的情况下) 的结果。如果在后台线程中抛出异常,get()
则将抛出它,包裹在ExecutionException
.
Please also note that overidden SwingWorker
methods are protected
and you don't need to make them public
.
另请注意,覆盖的SwingWorker
方法是protected
,您不需要 make 它们public
。
回答by hvgotcodes
You are right, you are violating the cardinal rule of Swing, which is don't modify the GUI anywhere except for on the event-dispatch-thread.
您是对的,您违反了 Swing 的基本规则,即不要在事件调度线程之外的任何地方修改 GUI。
If it was me, I would throw an event that the GUI listens for to show the error message. Or, you can just wrap the invocation of the SwingWorker in a try catch and show the dialogue there.
如果是我,我会抛出一个事件,GUI 侦听以显示错误消息。或者,您可以将 SwingWorker 的调用包装在 try catch 中并在那里显示对话。
回答by Juan Javier Triff Cabanas
First of all: sorry for the short answer, don't have too much time to spare.
首先:抱歉回答太短,没有太多空闲时间。
I had the same problem: wanting to publish to System.out
from within the worker.
我有同样的问题:想System.out
从工作人员内部发布。
Short answer: It won't block your app if you use the execute()
method
简短回答:如果您使用该execute()
方法,它不会阻止您的应用程序
The thing is that there is no blocking if you execute the worker as it should be: a background task.
问题是,如果您按原样执行工作程序,则不会发生阻塞:后台任务。
class MyWorker extend SwingWorker<Void, Void>{
@Override
protected Void doInBackground() throws ... {
// your logic here and a message to a stream
System.out.println("from my worker, with love");
// ...
try {
throw new Exception("Whoops, this is an exception from within the worker");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Now you will invoke this worker creating a new instance, and after that calling the execute()
method. But to save you some time: you will probably want to know when your worker is done, so you'll need to register an property change listener, which is fairly simple:
现在您将调用此工作程序创建一个新实例,然后调用该execute()
方法。但是为了节省您一些时间:您可能想知道您的工作人员何时完成,因此您需要注册一个属性更改侦听器,这非常简单:
class MyListener implements PropertyChangeListener{
@Override
public void propertyChange(PropertyChangeEvent evt){
if(evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)){
System.out.println("The worker is done");
}
}
}
And to put everything together at your main()
:
并将所有内容放在您的main()
:
public void main(...){
MyWorker w = new MyWorker();
MyListener l = new MyListener();
w.addPropertyChangeListener(l);
w.execute();
}