Я использую следующий подход для создания компонентов и возврата значений из Swing в/из вне EDT. Например, следующий метод может быть расширением JFrame для создания JPanel и добавления его к родительскому JFrame:
public JPanel threadSafeAddPanel() {
final JPanel[] jPanel = new JPanel[1];
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
jPanel[0] = new JPanel();
add(jPanel[0]);
}
});
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
}
return jPanel[0];
}
Локальный массив длиной 1 используется для передачи "результата" изнутри Runnable, который вызывается в EDT. Ну, это выглядит "немного" хакерским, и поэтому мои вопросы:
- Имеет ли это смысл? Кто-нибудь еще делает что-то подобное?
- Является ли массив 1 длины хорошим способом передачи результата?
- Есть ли более простой способ сделать это?
Ответ 2 · рейтинг 3 · jfpoilpret
Хотя этот метод может иметь смысл в некоторых ситуациях, в большинстве случаев он бесполезен.
Причина в том, что создание большинства (если не всех) ваших компонентов всегда будет происходить из EDT в результате действий пользователя (элемент меню или нажатие кнопки), которые всегда выполняются из EDT.
В тех случаях, когда вам нужно выполнить большую работу перед созданием панели, и вы не хотите блокировать EDT, вам следует, как предложил кто-то другой, использовать SwingWorker или платформу Swing, которые предлагают поддержку длительных задач (обычно на основе В любом случае SwingWorker внутренне, но не обязательно).
Что касается вашего вопроса 2, к сожалению, у вас не так много способов сделать это:
- Используйте массив из 1 элемента, как и вы, это самое простое, но и самое уродливое решение.
- Создайте класс ItemHolder (см. ниже), который делает почти то же самое, требует немного больше работы и, на мой взгляд, чище.
- Наконец, используйте возможности java.util.concurrent (Future и Callable); это было бы самым чистым, я думаю, но это также требует больше всего усилий
Вот упрощенный класс ItemHolder:
public class ItemHolder<T> {
public void set(T item) {...}
public T get() {...}
private T item;
}
Ответ 3 · KitsuneYMG
- а) Имеет смысл. б) не то, что я знаю.
- Так же хорош, как и любой.
- Создайте JPanel вне вызова
invokeAndWait
//Эта строка добавлена, чтобы успокоить уценку
public JPanel threadSafeAddPanel() {
final JPanel jPanel = new JPanel();
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
add(jPanel);
}
});
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
}
return jPanel;
}
Ответ 4 · Kevin Brock
Вы можете легко проверить, является ли текущий поток EDT, а затем выполнить правильно и проще в этом контексте. Что касается использования конечного массива для получения возвращаемого значения, это самый простой способ, когда вам нужно использовать анонимный внутренний класс, подобный этому.
public JPanel threadSafeAddPanel() throws InterruptedException,
InvocationTargetException {
if (EventQueue.isDispatchThread()) {
JPanel panel = new JPanel();
add(panel);
return panel;
} else {
final JPanel[] jPanel = new JPanel[1];
EventQueue.invokeAndWait(new Runnable() {
public void run() {
jPanel[0] = new JPanel();
add(jPanel[0]);
}
});
return jPanel[0];
}
}