Как я должен обрабатывать исключения при использовании SwingWorker? - PullRequest
22 голосов
/ 26 апреля 2009

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

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

Я особенно обеспокоен возможными исключениями InterruptedException. Пример 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();

Ответы [ 7 ]

11 голосов
/ 03 июня 2011

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

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

Кроме того, он генерирует очень специфическое непроверенное исключение - CancellationException. Конечно, он может потенциально генерировать любое непроверенное исключение, но CancellationException не является «исключительным» и неожиданным. Он выдается, когда вы пытаетесь вызвать метод get после того, как он был вызван отменить.

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

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

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

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

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

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

2 голосов
/ 02 мая 2009

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

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

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

2 голосов
/ 28 апреля 2009

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

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

2 голосов
/ 26 апреля 2009

Очень сильно зависит от типа ошибок, которые могут возникнуть в фоновом режиме. Если задание в doInBackground выдает исключение, оно будет передано методу done как вложенное ExecutionException. В этом случае лучше всего обрабатывать вложенное исключение, а не само ExecutionException.

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

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

0 голосов
/ 22 апреля 2017

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

@Override
protected void done()
{
    try
    {
        if(!super.isCancelled())
        {
            super.get();
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}
0 голосов
/ 26 апреля 2009

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

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

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

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

0 голосов
/ 26 апреля 2009

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

...