Swing Worker 中优雅的异常处理

2022-09-01 18:04:52

我正在通过 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 块,因为计算部分会引发异常,所以我认为这最终会更干净。


答案 1

正确的做法如下:

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()Voidget()ExecutionException

另请注意,overidden方法是,您不需要制作它们。SwingWorkerprotectedpublic


答案 2

一种选择是使用 SwingUtilities.invokeLater(...)EDT

SwingUtilities.invokeLater(new Runnable(){
    @Override
    public void run(){
        JOptionPane.showMessageDialog(
            Utils.getActiveFrame(),
            msg, 
            "Error", 
            JOptionPane.ERROR_MESSAGE,
            errorIcon);
    }
});

正如您所指出的,SwingWorker 能够报告中间结果,但您需要覆盖 process(...),当您调用 publish(...) 时会调用 process(...)。

无论如何,如果发生异常,为什么不只设置一个标志,如果设置了该标志,请在 中显示对话框,因为它在 ?done()EDT


推荐