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

Как обращаться с исключениями при использовании SwingWorker?

Я использую SwingWorker в Java 6, чтобы избежать запуска долгого кода в потоке отправки событий.

Если вызов метода get() в моем методе done() возвращает исключение, что является подходящим способом обработки исключения?

Я особенно обеспокоен возможными InterruptedExceptions. Пример JavaDoc просто игнорирует исключение, но с годами я узнал, что проглатывание исключений приводит к жесткому отлаживанию кода.

Пример использования шаблона выглядит следующим образом:

new SwingWorker<String, Void>() {

    @Override
    protected String doInBackground() throws Exception {
        // do long-running calculation
        return result;
    }

    @Override
    protected void done() {
        try {
            setTextField(get());
        } catch (InterruptedException e) {
            e.printStackTrace();  
        } catch (ExecutionException e) {
            e.printStackTrace();  
        }
    }
}.execute();
4b9b3361

Ответ 1

Это старое сообщение, но я хочу сделать некоторые пояснения:

SwingWorker.get throws InterruptedException, ExecutionException как проверенные исключения.

Кроме того, он генерирует очень конкретное исключение, исключающее исключение CancellationException. Конечно, это потенциально может вызвать любое исключение, но CancellationException не является "исключительным" и неожиданным. Он бросается, когда вы пытаетесь вызвать метод get после того, как он был вызван cancel.

ExecutedException выбрасывается, когда исключение исключается внутри doInBackground. Исходное исключение завершается внутри ExecutionException. Когда метод get() будет вызываться, будет выведено ExecutionException. Идея вынуть оригинальное исключение и управлять, это хорошо. (Как указал Эмиль Х).

Исключение CancellationException не проверено и, на мой взгляд, должно быть проверено. Единственным оправданием для реализации API, которое не было проверено, является то, что у него есть метод состояния isCancelled().
Вы можете:
- test isCancelled(), и если true, не вызывайте get(), поскольку он будет генерировать CancellationException
- объединить get() с try-catch и добавить исключение CancellationException, так как unchecked не будет запрашиваться компилятором
- тот факт, что CancellationException не проверен, позволяет вам забыть все это и получить приятный сюрприз.
- сделайте все, потому что вы не отмените работника -

InterruptedException. Если вы отмените SwingThread с отменой (true) первого прерывания вызова метода (наверняка Thread.sleep, this.wait, может быть, некоторые методы ввода-вывода) в doInBackground будет вызывать InterruptException. Но это исключение не завершено в ExecuteException. DoInBackground остается для прерывания с прерванным исключением. Если он уловлен и преобразован в какое-то другое исключение, они будут проигнорированы, так как к этому моменту отмена уже вызвала SwingThread.done на EDT, и если это было сделано, вызвало get, оно получило только стандартное исключение CancellationException. Не прерывается Исключение!

Если вы отмените отмену (false), в doInBackground не возникает исключение прерывания. То же самое, если вы отмените отмену (true), но внутри doInBackground нет прерываемых вызовов методов. В этих случаях doInBackground будет следовать своему естественному циклу. Этот цикл должен проверить метод isCancelled и выйти изящно. Если doInBackground этого не сделает, он будет работать вечно. Я не тестировал наличие тайм-аутов, но я не верю в это.

Для меня он остается только серой областью. В каких случаях InterruptedException выбрасывается get? Я бы хотел увидеть короткий код, так как я не мог создать подобное исключение.: -)

P.S. Я задокументировал в другом Вопросе и Ответ, что прослушиватель изменений состояния и состояния в случае отмены вызван до выхода doInBackground. Поскольку это верно, это - не ошибка, - требует особого внимания при разработке метода doInBackground. Если вас это интересует, см. SwingWorker: когда именно вызывается метод done?

Ответ 2

Это зависит от типа ошибок, которые могут возникнуть в результате задания фона. Если задание в doInBackground выдает исключение, оно будет передано готовому методу как вложенное ExecutionException. Наилучшей практикой в ​​этом случае будет обработка вложенного исключения, а не самого ExecutionException.

Например: если рабочий поток генерирует исключение, указывающее на то, что соединение с базой данных было потеряно, вы, вероятно, захотите повторно подключить и перезапустить задание. Если работа, которая должна быть выполнена, зависит от какого-то ресурса, который, как оказалось, уже используется, было бы неплохо предложить повторить попытку или отменить выбор. Если возникшее исключение не имеет каких-либо последствий для пользователя, просто зарегистрируйте ошибку и продолжайте.

Из того, что я помню, я считаю, что InterruptedException здесь не будет проблемой, так как вы вызываете вызов метода get в методе done, так как InterruptedException будет вызываться только в том случае, если вызов get прерывается, ожидая фоновая работа для завершения. Если бы такое непредвиденное событие произошло, вы, вероятно, захотите отобразить сообщение об ошибке и выйти из приложения.

Ответ 3

Это вопрос интерфейса, так как это вопрос обработки ошибок. Многие приложения добавляют небольшую таблицу, в которой перечислены текущие фоновые задания. Исключение в одном из них может мигать строка таблицы, которая вызвала ошибку, или сделать что-то столь же разрушительное, как и предупреждение. Это зависит от серьезности исключения. Я думаю, что более сложный вопрос, который вам, вероятно, придется ответить, - это то, как много разных типов исключений мы говорим, и какова их относительная серьезность.

Я думаю, что простой компромисс может состоять в том, чтобы представить модальное предупреждение о самых серьезных ошибках и что-то еще, просто записать это событие до тех пор, пока оно не будет блокировать пользователя от продолжения или b) пользователь закрывает документ/окно, в этот момент вы можете показать список исключений, которые произошли во время задач фоновой обработки, в то же время, когда вы спрашиваете, хотите ли вы, например, сохранить несохраненные буферы.

Ответ 4

Я бы рекомендовал, чтобы ошибка распространялась вплоть до начала действия.

Например, если пользователь нажимает кнопку для извлечения данных из источника данных. Если возникает проблема, будь то ошибка учетных данных, сетевая ошибка, ошибка базы данных или что-то еще, вам не было бы целесообразно иметь логику внутри этого рабочего потока, чтобы попытаться решить ее прямо там.

Но если вы разрешите его распространять туда, где была запущена задача, оттуда вы можете предпринять соответствующие исправления ошибок, например, снова ввести диалог учетных данных, показывая диалог "попробуйте еще раз" или даже показывая сообщение об ошибке.

Ответ 5

Предположим, что это приложение графического интерфейса, вы можете предоставить визуальную обратную связь о неудачной операции после исключения.

Ответ 6

Я должен был уточнить свой первоначальный пост. Не просто улавливайте определенные типы исключений в методе done(). Любой код, который выполняется в doInBackground(), может генерировать любой тип исключения, и только улавливание Исключения в вашем вопросе может привести к тому, что Исключения будут выбрасываться на EDT (Thread Dispatch Thread или основной поток GUI). Улавливание всех типов исключений в методе done() является хорошей привычкой при использовании SwingWorkers.

@Override
protected void done()
{
    try
    {
        if(!super.isCancelled())
        {
            super.get();
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}

Ответ 7

Я думаю, вы не получите много из этих вопросов с С#. Вам нужно понять, что такое исключение, и справиться с ним соответствующим образом (что обычно позволяет ему продвигаться дальше по стеку).

InterruptedException - Бросок, когда поток прерывается (Thread.interrupt) при ожидании (примерно). Почему вы хотите, чтобы поток был прерван? Обычно вы хотите, чтобы поток остановил свое действие - reset состояние прерывания и выход. Например, PlugIn прервет потоки апплетов, если они продолжатся намного дольше после того, как апплет исчезнет. Однако в этом случае при условии, что done вызывается правильно, вы не должны ждать вообще. Поэтому было бы целесообразно обернуть исключение в IllegalStateException (и API-документы должны, вероятно, указать это). Это действительно плохой API. Режим publish/process, вероятно, имеет больше смысла.

ExecutionException - Вам нужно иметь дело с обернутым исключением. Если вы не ожидаете определенного типа исключения, оберните его в неконтролируемое исключение.

В общем, я бы предложил четкое разделение между тем, что происходит на EDT, и тем, что происходит с EDT. Поэтому избегайте SwingWorker в производственном коде.