Часто пользователь может встретить ситуацию, когда программа, обладающая толстым интерфейсом, реализованным на SWING во время выполнения каких-либо операций, перестает реагировать на действия пользователя. Все просто. Программист реализовал логику программы в потоке, в котором производится отрисовка и отслеживание событий пользовательского интерфейса. Соответственно пользователь, допустим, нажимает на кнопку, вызывается «мощная» процедура, реализующая некоторую логику, программа виснет до момента возвращения управления из процедуры обратно в GUI. Как быть?
Очевидное решение – создать новый поток выполнения для процедуры, которая будет реализовывать логику. При этом остаются некоторые существенные проблемы. Это в первую очередь отслеживание в потоке GUI результата выполнения процедуры. Вторым моментом является необходимость отслеживания прогресса выполнения процедуры и возможно получения промежуточных результатов выполнения. Ну, все не так сложно и вполне реализуемо, тем более поддержка многопоточности в JAVA неплохо развита. Только с 6 версии JAVA это будет очевидным изобретением велосипеда. Да, да я в курсе, что реализации SwingWorker есть и для более ранних версий, и обязательно рассмотрю различия в будущих статьях.
Итак, чтобы было интереснее разбирать возможности SwingWorker, реализованного в Java 6, набросаем небольшую программу. Пусть программа выводит на экран диалоговое окно с одной кнопкой:
Кнопка производит запуск некоторой длительной процедуры, например такой:
public int doSomeWork()
{
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException ex) {}
return 1;
}
Зададим SLEEP_TIME секунд 10. Нажмем на кнопку и допустим немного увеличим размер окна. В результате получим в течении 10 секунд после нажатия на кнопку что-нибудь подобное:
Мало того, мы даже не сможем закрыть это окно в течение указанного периода. То есть программа не может реагировать на события в течении 10 секунд, т.к. в том же потоке, где обрабатываются события, мы запустили некую процедуру, которая пока не закончит свою работу не даст программе ни на что реагировать.
Давайте попробуем пристроить SwingWorker в нашу программу таким образом, чтобы процедура не блокировала реакцию программы на события.
Взглянем на документацию API Java 6, в той части, что описывает класс SwingWorker. В самом начале так и написано, что данный класс «Абстрактный класс для выполнения длительных взаимодействующих с GUI задач в отдельном потоке». Как раз то, что нам и надо. Подкласс абстрактного класса SwingWorker должен реализовать метод doInBackground () для выполнения вычислений в фоновом потоке. Ну и реализуем:
public class BigBlackBox1 extends SwingWorker<Integer, Object> {
private static final long SLEEP_TIME=10000;
public int doSomeWork()
{
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException ex) {}
return 1;
}
@Override
protected Integer doInBackground() {
return new Integer(doSomeWork());
}
}
Вызов соответственно будет похож на что-то вроде:
private void actionForOurButton(java.awt.event.ActionEvent evt) {
BigBlackBox1 bbb1=new BigBlackBox1();
bbb1.execute();
}
Теперь работа doSomeWork не блокирует обработку событий приложением.
Скажу пару слов как о параметрах класса и как отследить момент и результат выполнения класса.
Параметров у класса два. Первый определяет тип возвращаемых значений по окончании выполнения нашего потока, а второй тип значений промежуточных результатов.
Вы можете определить метод done() в реализованном вами подклассе. Он будет вызван, когда doInBackground () завершит свою работу. Ничего страшного, если вы реализуете анонимный класс на базе своего подкласса, реализуете в нем метод done() и будете из него производить вызовы методов GUI компонентов. Необходимо сразу отметить, что вызовы методов GUI компонентов приведут к созданию деятельности в потоке GUI, а не в том, в котором происходит работа done(). Ну или передавайте хэндлеры на GUI объекты в конструктор вашего подкласса и организуйте над ними работу в done(). В SwingWorker есть так же метод get() который позволяет получить результат выполнения потока SwingWorker. get() имеет две реализации. В первой случае мы вызываем метод get() и ждем, когда закончится выполнение doInBackground (), фактически блокируя GUI поток, и есть метод get (long timeout, TimeUnit unit) который будет ждать лишь указанное время, а потом вернет управление через InterruptedException. Лучше всего вызывать метод get() или в методе done(), который сам по себе вызывается после окончания работы doInBackground(). Или вызвать пару методов isCanceled() и isDone(). Первый из указанных методов возвращает true, если поток SwingWorker был прерван преждевременно, а isDone() возвращает true в том случае, если SwingWorker завершился нормально, отработав doInBackground ().
Аспекты получения и отображения промежуточных результатов рассмотрим в других статьях.
Остались вопросы? Пишите.
Все торговые марки, упомянутые в статье, являются исключительной собственностью их владельцев.
отличная статья, понятная
хотелось бы больше узнать про «аспекты получения и отображения промежуточных результатов» в JProgressBar