Поток диспетчеризации событий
Поток диспетчеризации событий (EDT) — это фоновый поток, используемый в Java для обработки событий из Abstract Window Toolkit (AWT) графического пользовательского интерфейса очереди событий . Это пример общей концепции событийно-ориентированного программирования , которая популярна во многих других контекстах, помимо Java, например, в веб-браузерах или веб-серверах .
События — это в первую очередь события обновления, которые заставляют компоненты пользовательского интерфейса перерисовываться, или события ввода с устройств ввода, таких как мышь или клавиатура. AWT использует однопоточную модель рисования , в которой все обновления экрана должны выполняться из одного потока. Поток диспетчеризации событий — единственный допустимый поток, обновляющий визуальное состояние видимых компонентов пользовательского интерфейса. Обновление видимых компонентов из других потоков является источником многих распространенных ошибок в программах Java , использующих Swing . [1] Поток диспетчеризации событий называется первичным рабочим в Adobe Flash и потоком пользовательского интерфейса в SWT , .NET Framework и Android .
Цикл сообщений для сериализации обращений к графическому интерфейсу
[ редактировать ]Программное приложение обычно состоит из нескольких потоков и одной структуры данных GIT. Это означает, что GIT — это общая структура данных, и необходима некоторая синхронизация, чтобы гарантировать, что только один поток имеет доступ к ней одновременно. Хотя AWT и Swing предоставляют методы ( небезопасные для потоков ) для создания и доступа к компонентам графического пользовательского интерфейса, и эти методы видны всем потокам приложений, как и в других средах графического пользовательского интерфейса, только один поток диспетчеризации событий имеет право выполнять эти методы. [2] [3] [4] Поскольку программисты часто пропускают это требование, сторонние Look and Feels , такие как Substance, доходят до того, что отказываются создавать экземпляры любого компонента Swing, когда он не работает в потоке диспетчеризации событий. [5] чтобы предотвратить такую ошибку кодирования. Доступ к графическому интерфейсу сериализуется, и другие потоки могут отправлять некоторый код для выполнения в EDT через очередь сообщений EDT .
То есть, как и в других средах графического пользовательского интерфейса, поток диспетчеризации событий тратит свою жизнь на перекачку сообщений: он поддерживает очередь сообщений о действиях, которые необходимо выполнить через графический интерфейс. Эти запросы отправляются в очередь системой и любым потоком приложения. EDT использует их один за другим и в ответ обновляет компоненты графического интерфейса. Сообщения могут представлять собой известные действия или включать обратные вызовы, ссылки на пользовательские методы, которые должны быть выполнены с помощью EDT.
Важным требованием, предъявляемым ко всем сообщениям, является то, что они должны выполняться быстро, чтобы графический интерфейс оставался отзывчивым. В противном случае цикл сообщений блокируется и графический интерфейс зависает.
Отправка кода пользователя в EDT
[ редактировать ]Существуют различные решения для отправки кода в EDT и выполнения длительных задач без блокировки цикла.
Обработчики событий компонентов (прослушиватели)
[ редактировать ]Компоненты графического пользовательского интерфейса поддерживают списки обратных вызовов, называемые прослушивателями, которые обычно заполняются при создании компонентов. EDT запускает прослушиватели, когда пользователь каким-либо образом возбуждает компоненты (нажимается кнопка, перемещается мышь, выбирается элемент, теряется фокус, изменяется размер компонента и т. д.).
Таймер
[ редактировать ]Для коротких задач, которые требуют доступа к графическому интерфейсу пользователя или его изменения периодически или в определенное время, javax.swing.Timer
используется. Его можно рассматривать как невидимый компонент графического интерфейса, прослушиватели которого зарегистрированы для срабатывания в определенное время.
Эквиваленты
System.Windows.Forms.Timer
- .NET Frameworkflash.utils.Timer
- Адобе Флэш
Запросы из других тем
[ редактировать ]Другие потоки приложения могут передавать некоторый код для выполнения в потоке диспетчеризации событий с помощью SwingUtilities
вспомогательные классы (или EventQueue
если вы используете AWT ). Отправленный код должен быть обернут тегом Runnable
объект. Два метода этих классов позволяют:
- синхронное выполнение кода (
SwingUtilities.invokeAndWait(Runnable)
илиEventQueue.invokeAndWait(Runnable)
) - и асинхронное выполнение кода (
SwingUtilities.invokeLater(Runnable)
илиEventQueue.invokeLater(Runnable)
)
из потока диспетчеризации событий.
Метод invokeAndWait()
никогда не следует вызывать из потока диспетчеризации событий — он выдаст исключение . Метод SwingUtilities.isEventDispatchThread()
или EventQueue.isDispatchThread()
может быть вызван, чтобы определить, является ли текущий поток потоком диспетчеризации событий.
Код, предоставленный с помощью invokeLater
и invokeAndWait
до EDT должно быть как можно быстрее, чтобы не допустить замерзания. Обычно они предназначены для доставки результатов длительных вычислений в графический интерфейс пользователя (пользователя).
Шаблон проектирования работника
[ редактировать ]Как выполнение задачи в другом потоке, так и представление результатов в EDT можно совместить с помощью шаблона проектирования Worker . javax.swing.SwingWorker
Класс, разработанный Sun Microsystems , является реализацией шаблона проектирования Worker и, начиная с Java 6, является частью стандартного дистрибутива Swing. SwingWorker обычно вызывается из прослушивателя событий, исполняемых EDT, для выполнения длительной задачи, чтобы не блокировать EDT.
Образцы
[ редактировать ]SwingWorker<Document, Void> worker = new SwingWorker<Document, Void>() {
public Document doInBackground() throws IOException {
return loadXML(); // heavy task
}
public void done() {
try {
Document doc = get();
display(doc);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
worker.execute();
Если вы используете Groovy и groovy.swing.SwingBuilder
, вы можете использовать doLater()
, doOutside()
, и edt()
. Тогда можно написать проще так:
doOutside {
def doc = loadXML() // heavy task
edt { display(doc) }
}
Эквиваленты
[ редактировать ]System.ComponentModel.BackgroundWorker
- .NET Frameworkflash.system.Worker
- Адобе Флэшandroid.os.AsyncTask
- Андроид
Модальное исполнение
[ редактировать ]SwingWorker обычно создается EDT для длительных задач при обработке событий обратного вызова (прослушивателя). Создавая рабочий поток, EDT продолжает обработку текущего сообщения, не дожидаясь завершения рабочего потока. Часто это нежелательно.
Часто ваш EDT обрабатывает действие компонента графического пользовательского интерфейса, которое требует от пользователя сделать выбор с помощью другого диалогового окна, такого как JFileChooser, которое появляется, остается отзывчивым, пока пользователь выбирает свой вариант, и действие продолжается с выбранным файлом только после кнопки «ОК». нажимается. Видите ли, это требует времени (пользователь отвечает в течение нескольких секунд), и вам нужен отзывчивый графический интерфейс (сообщения все еще перекачиваются в EDT) в течение всего этого времени, пока EDT блокируется (он не обрабатывает новые сообщения, например JFileChooser, в очередь до закрытия диалогового окна и завершения текущего действия компонента). Порочный круг разрывается посредством входа EDT в новый цикл сообщений, который отправляет сообщения как обычно до тех пор, пока не придет «модальный диалог завершится» и нормальная обработка сообщения не возобновится с заблокированной позиции в действии компонента.
с открытым исходным кодом Проект Foxtrot эмулирует перекачку цикла сообщений Swing, чтобы обеспечить «синхронный» механизм выполнения произвольных пользовательских задач, который продолжается только после того, как работник завершит задачу.
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
button.setText("Sleeping...");
String text = null;
try
{
text = (String)Worker.post(new Task()
{
public Object run() throws Exception
{
Thread.sleep(10000);
return "Slept !";
}
});
}
catch (Exception x) ...
button.setText(text);
somethingElse();
}
});
Начиная с Java 1.7, Java предоставляет стандартное решение для пользовательских циклов вторичных сообщений , предоставляя createSecondaryLoop () в системной EventQueue ().
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Эта проблема характерна не только для Java Swing . Та же проблема существует в большинстве наборов инструментов Widget , например, в Windows Forms , где класс BackgroundWorker выполняет ту же цель, что и SwingWorker в Java.
- ^ «Тема отправки событий» . Сан Микросистемс . Проверено 2 октября 2011 г.
- ^ «Отладка Swing — это действительно сложно?» . Александр Поточкин. Архивировано из оригинала 5 августа 2011 г. Проверено 2 октября 2011 г.
- ^ «Первоначальные темы» . Сан Микросистемс . Проверено 2 октября 2011 г.
- ^ «Более строгие проверки нарушений EDT в Substance · Pushing Pixels» .
Внешние ссылки
[ редактировать ]javax.swing
Swing API Javadoc ) ( документацияjava.awt
API AWT (документация Javadoc )- Документация по API Swing
- Поток диспетчеризации событий
- Описание SwingWorker из руководства по Swing
- Статья об обработке событий AWT/Swing о перекачке, отправке и обработке событий, а также EDT
- Домашняя страница проекта «Фокстрот»