J'ai le code qui détecte si la file d'attente d'événements Java AWT est gelé (occupé à traiter un événement ou en attente d'une serrure) pour une quantité excessive de temps, en raison de bogué code étranger qui ne parvient pas à utiliser SwingWorker
ou similaire, et je veux offre de récupérer. Il suffit de tuer le fil d'expédition de l'événement avec Thread.stop
œuvres, mais il pourrait être dangereux (et EQ peut être bloqué simplement parce que l'ordinateur est temporairement surchargé), donc je préférerais demander à l'utilisateur pour confirmation. Cependant, l'affichage d'un dialogue nécessite d'attendre que l'égalisation soit débloquée, ce qui est exactement ce qui n'est pas possible.Comment afficher la boîte de dialogue Java en utilisant une autre file d'attente d'événements?
Est-il possible, à partir d'un programme Java raisonnablement portable, pour afficher une boîte de dialogue (ou vraiment tout type d'élément interface utilisateur qui peut répondre à des événements d'entrée) sans impliquer la file d'attente normale?
J'ai déjà essayé d'exécuter SystemTray.add(TrayIcon)
, qui ne parvient pas à afficher un élément de la liste lorsqu'il est appelé depuis un autre thread ... mais l'icône n'est pas peinte et ActionEvent
s ne sont pas livrés, donc cela n'est pas utile.
Il semble également possible d'afficher un nouveau JFrame
et même de peindre une étiquette en utilisant paintImmediately
, mais il n'y a toujours aucun moyen apparent de recevoir des événements de souris.
Après l'indice de Tom Hawtin, j'ai essayé de créer un nouveau AppContext
. Sur JDK 6u18, une boîte de dialogue présentée dans le nouveau contexte semble peindre correctement, mais ne reçoit pas les événements de la souris jusqu'à ce que la file d'attente d'événement est débloquée, défaisant le but:
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() {}
}
La solution correcte consiste à ne pas écrire du code qui bloque la file d'attente des événements. Cela devrait tuer le thread et la mémoire de vidage dans un environnement de débogage, et ne rien faire en production. –
Au lieu d'essayer de reconfigurer le mécanisme de peinture AWT, il serait préférable d'utiliser l'égaliseur de manière appropriée - fermez les processus longs vers d'autres threads et évitez les opérations de verrouillage. – akf
Je suis d'accord avec les 2 commentaires précédents.Il n'y a rien de mal avec la file d'attente ou la gestion des événements qui n'est pas causée par des erreurs dans le codage de l'application. –