Подтвердить что ты не робот

Изящная обработка исключений в 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, поскольку вычислительная часть генерирует исключения, поэтому я думаю, что в конце это чище.

4b9b3361

Ответ 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 и показать диалог там.