即使使用包装类,SwingWorkerexception也会丢失

我一直在努力解决SwingWorker的可用性问题,该问题包含后台任务中抛出的任何exception,例如, 在此SO线程中描述的。 该线程给出了问题的一个很好的描述,但没有讨论恢复原始exception。

我已被传递的applet需要向上传播exception。 但我甚至无法抓住它。 我正在使用此博客条目中的SimpleSwingWorker包装器类来尝试解决此问题。 这是一个相当小的课程,但我会在最后重新发布它仅供参考。

调用代码看起来很像

try { // lots of code here to prepare data, finishing with SpecialDataHelper helper = new SpecialDataHelper(...stuff...); helper.execute(); // this will call get+done on the actual worker } catch (Throwable e) { // used "Throwable" here in desperation to try and get // anything at all to match, including unchecked exceptions // // no luck, this code is never ever used :-( } 

包装纸:

 class SpecialDataHelper extends SimpleSwingWorker { public SpecialDataHelper (SpecialData sd) { this.stuff = etc etc etc; } public Void doInBackground() throws Exception { OurCodeThatThrowsACheckedException(this.stuff); return null; } protected void done() { // called only when successful // never reached if there's an error } } 

SimpleSwingWorker的function是自动调用实际的SwingWorker的done()/get()方法。 理论上,这会重新抛出后台发生的任何exception。 在实践中,没有任何东西被捕获,我甚至不知道为什么。

SimpleSwingWorker类,供参考,为简洁起见,没有任何简要说明:

 import java.util.concurrent.ExecutionException; import javax.swing.SwingWorker; /** * A drop-in replacement for SwingWorker but will not silently * swallow exceptions during background execution. * * Taken from http://jonathangiles.net/blog/?p=341 with thanks. */ public abstract class SimpleSwingWorker { private final SwingWorker worker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { SimpleSwingWorker.this.doInBackground(); return null; } @Override protected void done() { // Exceptions are lost unless get() is called on the // originating thread. We do so here. try { get(); } catch (final InterruptedException ex) { throw new RuntimeException(ex); } catch (final ExecutionException ex) { throw new RuntimeException(ex.getCause()); } SimpleSwingWorker.this.done(); } }; public SimpleSwingWorker() {} protected abstract Void doInBackground() throws Exception; protected abstract void done(); public void execute() { worker.execute(); } } 

忘记你的包装器,它吃exception,而SwingWorker却没有。 以下是如何使用SwingWorker,以及如何处理从后台任务抛出的特定exception:

 class MeaningOfLifeFinder extends SwingWorker { @Override public String doInBackground() throws SomeException { return findTheMeaningOfLife(); } @Override protected void done() { // called in the EDT. You can update the GUI here, show error dialogs, etc. try { String meaningOfLife = get(); // this line can throw InterruptedException or ExecutionException label.setText(meaningOfLife); } catch (ExecutionException e) { Throwable cause = e.getCause(); // if SomeException was thrown by the background task, it's wrapped into the ExecutionException if (cause instanceof SomeException) { // TODO handle SomeException as you want to } else { // the wrapped throwable is a runtime exception or an error // TODO handle any other exception as you want to } } catch (InterruptedException ie) { // TODO handle the case where the background task was interrupted as you want to } } } 

包装似乎按预期工作。 但是,如果发生exception,其实现将永远不会调用done() 。 这不适用于许多情况。 在done()调用get()可能更简单。 这将抛出doInBackground()中发生的任何exception。

不确定您的示例是如何构造的,但如果没有EDT,它在应用程序中不起作用。 因此,在SwingUtilities.invokeLater包装工作程序执行确实有帮助,即:

 SwingUtilities.invokeLater(new Runnable() { public void run() { new SpecialDataHelper().execute(); } }); 

以下示例确实打印了exception堆栈跟踪:

 public class Tester { static class SpecialDataHelper extends SimpleSwingWorker { public SpecialDataHelper () { } public Void doInBackground() throws Exception { throw new Exception("test"); } protected void done() { } } public static void main(String[] args) { try{ SwingUtilities.invokeLater(new Runnable() { public void run() { new SpecialDataHelper().execute(); } }); } catch(Exception ex){ ex.printStackTrace(); } } } 

还要考虑这个简单的示例,演示如何在不使用包装器的情况下获取doInBackground()中发生的exception。 如果忘记调用get() ,包装器只是一个帮助器。

 import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; public class Tester { static class Worker extends SwingWorker { @Override protected Void doInBackground() throws Exception { throw new Exception("test"); } @Override protected void done() { try { get(); JOptionPane.showMessageDialog(null, "Operation completed"); } catch (Exception ex) { JOptionPane.showMessageDialog(null, "Operation failed"); } } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Worker().execute(); } }); } } 

我想每个人都会让这个过于复杂。 试试这个:

 String myResult="notSet"; try { // from your example above helper.execute(); // this will call get+done on the actual worker myResult=helper.get(); } catch (Exception e) { // this section will be invoked if your swingworker dies, and print out why... System.out.println("exception "); e.printStackTrace() ; myResult="Exception "+e.getMessage(); } return myResult; 

你会抛出但被吃掉的例外。 有两点可以解释为什么会这样。 一,你只能从调用线程中捕获远程exception,你可以得到.get()结果。 更详细:要使上面的示例异步,只需在代码中向上移动.execute()。 您将发现远程exception的那一刻是在异步线程被轰炸之后,您.get()结果。 第二,通过捕获所有exception,您将捕获所有特殊的和子类化的exception,您可能已经构建了调用程序可能不知道的exception。