Swing Worker中的优雅异常处理

时间:2011-06-29 15:59:25

标签: java swing exception-handling

我通过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()方法在这里是一个很好的领导?

修改

是这样的:

} 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块,因为计算部分会抛出异常,所以我认为最终会更清晰。

4 个答案:

答案 0 :(得分:56)

正确的方法如下:

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

答案 1 :(得分:13)

一种选择是使用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 :(得分:1)

你是对的,你违反了Swing的基本规则,除了事件派遣线程之外,它不会修改任何地方的GUI。

如果是我,我会抛出一个GUI监听的事件来显示错误信息。或者,您可以将SwingWorker的调用包装在try catch中并在那里显示对话框。

答案 3 :(得分:0)

首先:对不起,简短的回答,没有太多的时间。

我遇到了同样的问题:希望从工作人员中发布到System.out

简短回答:如果您使用execute()方法,则不会阻止您的应用

问题是如果你按原样执行工作程序就没有阻塞:后台任务。

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()); 
    }
  }
}

现在,您将调用此worker创建一个新实例,然后调用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");
    }
  }
}

并将所有内容放在main()

public void main(...){
  MyWorker w = new MyWorker();
  MyListener l = new MyListener();
  w.addPropertyChangeListener(l);
  w.execute();
}