У меня есть код, который определяет, зависла ли очередь событий Java AWT (занята обработкой какого-либо события или ожидает блокировки) в течение чрезмерного времени из-за ошибочного внешнего кода, который не может использовать SwingWorker
или аналогичный, и я хочу предложить восстановить. Простое уничтожение потока отправки событий с помощью Thread.stop
работает, но это может быть опасно (и EQ может быть заблокирован просто потому, что компьютер временно перегружен), поэтому я бы предпочел запрашивать подтверждение у пользователя. Однако отображение диалога требует ожидания разблокировки эквалайзера, что как раз и невозможно.
Есть ли способ из достаточно переносимой Java-программы отображать диалоговое окно (или действительно любой элемент пользовательского интерфейса, который может реагировать на входные события) без участия обычной очереди событий?
Я уже пытался запустить SystemTray.add(TrayIcon)
, который может отображать элемент в трее при вызове из другого потока... но значок не рисуется, а ActionEvent
не доставляются, так что это бесполезно.
Также кажется возможным отобразить новый JFrame
и даже нарисовать на нем метку с помощью paintImmediately
, но пока нет очевидного способа получать события мыши.
Следуя подсказке Тома Хоутина, я попытался создать новый AppContext
. В JDK 6u18 диалоговое окно, отображаемое в новом контексте, выглядит правильно, но не получает события мыши, пока основная очередь событий не будет разблокирована, что противоречит цели:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public @Override void run() {
new MainWindow().setVisible(true);
System.err.println("main context: " + AppContext.getAppContext());
}
});
new TrackEQ(1000*3);
}
private static class MainWindow extends JFrame {
MainWindow() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JButton pause = new JButton("Pause");
pause.addActionListener(new ActionListener() {
public @Override void actionPerformed(ActionEvent e) {
try {
Thread.sleep(15000);
} catch (InterruptedException x) {
x.printStackTrace();
}
}
});
getContentPane().add(pause);
pack();
setLocation(100, 100);
}
}
private static class TrackEQ implements Runnable {
private final ScheduledExecutorService svc;
private final int timeout;
private boolean stuck = false;
private boolean wait = false;
private Thread eq;
TrackEQ(int timeout) {
this.timeout = timeout;
svc = Executors.newSingleThreadScheduledExecutor();
svc.schedule(this, 0, TimeUnit.MILLISECONDS);
}
public @Override synchronized void run() {
if (EventQueue.isDispatchThread()) {
stuck = false;
eq = Thread.currentThread();
} else {
if (stuck && !wait) {
System.err.println("UI is stuck!");
wait = true;
Map<Thread,StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
StackTraceElement[] stack = stackTraces.get(eq);
if (stack != null) {
for (StackTraceElement el : stack) {
System.err.println("stuck at " + el);
}
ThreadGroup grp = new ThreadGroup("showing dialog");
grp.setDaemon(true);
new Thread(grp, new Runnable() {
public @Override void run() {
System.err.println("created new app context in " + Thread.currentThread().getThreadGroup());
SunToolkit.createNewAppContext();
EventQueue.invokeLater(new Runnable() {
public @Override void run() {
System.err.println("main EQ=" + eq + " whereas my EQ=" + Thread.currentThread());
System.err.println("will show dialog in " + AppContext.getAppContext());
final JDialog dlg = new JDialog();
dlg.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JButton fix = new JButton("Fix!");
fix.addActionListener(new ActionListener() {
@SuppressWarnings("deprecation")
public @Override void actionPerformed(ActionEvent e) {
System.err.println("agreed to fix");
eq.stop();
wait = false;
dlg.setVisible(false);
}
});
dlg.getContentPane().add(fix);
dlg.pack();
dlg.setLocation(200, 100);
dlg.setVisible(true);
System.err.println("showed dialog");
}
});
}
}, "showing dialog").start();
} else {
System.err.println("no stack trace for " + eq + "; listed threads: " + stackTraces.keySet());
}
} else {
stuck = true;
}
EventQueue.invokeLater(this);
svc.schedule(this, timeout, TimeUnit.MILLISECONDS);
}
}
}
private Main() {}
}
2 ответа
API-интерфейсы AWT написаны так, как если бы существовала только очередь событий. Итак, мы говорим об изменяемой статике и, следовательно, о плохом дизайне и злых хаках.
Подключаемый модуль Sun и WebStart используют недокументированный API для взлома контекста. Ознакомьтесь с классом AppContext
. Сначала контекст ищется с помощью ThreadGroup
и, если это неубедительно, затем путем проверки ClassLoader
в стеке.
Очевидные комментарии: вы, конечно, можете запустить отдельный процесс. Большинство приложений просто следят за тем, чтобы они не блокировали важные ресурсы, включая EDT.
Класс SwingWorker доступен в некоторых версиях Java.
Это позволяет вам запускать задачи, потребляющие три раза, в отдельном потоке.
В зависимости от того, какую версию JRE вы используете, вы можете подключить класс для запуска существующих задач в отдельных потоках.
Эти примеры взяты из Википедии:
До Java 6:
SwingWorker worker = new SwingWorker() {
public Object construct() {
... //add the code for the background thread
}
public void finished() {
... //code that you add here will run in the UI thread
}
};
Или для Java 6:
SwingWorker worker = new SwingWorker<Document, Void>() {
public Document doInBackground() {
Document intDoc = loadXML();
return intDoc;
}
};
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.