2010-01-17 4 views
4

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() {} 
} 
+4

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. –

+2

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

+0

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. –

Répondre

0

La classe SwingWorker est disponible dans certaines versions de Java .

Il vous permet d'exécuter vos tâches de trime consommatrice sur un thread séparé.

Selon la version de JRE que vous utilisez, vous pouvez connecter la classe pour exécuter vos tâches existantes sur des threads distincts.

Ces exemples sont de Wikipedia:

Pre-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 
    } 

}; 

Ou pour Java 6:

SwingWorker worker = new SwingWorker<Document, Void>() { 
    public Document doInBackground() { 
     Document intDoc = loadXML(); 
     return intDoc; 
    } 
}; 
+1

Le backport de SwingWorker est compatible avec la version actuelle: https://swingworker.dev.java.net/ – trashgod

+0

Je connais SwingWorker mais ce n'était pas la question. –

2

Les API AWT sont écrites comme s'il n'y avait qu'une file d'attente d'événements . Nous parlons donc de statique modifiable et donc de mauvaise conception et de hacks maléfiques.

Le Soleil et PlugIn WebStart utilisent une API non documentée pour pirater dans un contexte. Découvrez la classe AppContext. Le contexte est d'abord recherché par ThreadGroup et, si cela n'est pas concluant, en examinant ClassLoader sur la pile.

Commentaires évidents: Vous pouvez bien sûr lancer un processus séparé. La plupart des applications s'assurent juste qu'elles ne bloquent pas les ressources critiques, y compris l'EDT.

+0

Cela ressemble à la bonne direction; AppContext est ce que je recherchais, et le code pour AppletClassLoader et SunToolkit ressemble au bon modèle. (Il est clair que ce code va être spécifique au Sun JRE, donc une application portable devrait inclure une sorte d'EQ de secours sans aucune invite.) Malheureusement, cela ne fonctionne pas complètement: une boîte de dialogue s'affiche dans le contexte de l'autre application. peint bien mais ne reçoit aucun événement d'entrée jusqu'à ce que la file d'attente d'événement principal reprenne. –

Questions connexes