2011-01-04 7 views
5

Ich verwende häufig SwingUtilities.invokeLater(). Dies erschwert jedoch in bestimmten Fällen das Debuggen: Sie können keine Stack-Ablaufverfolgung des Codes mit dem Namen SwingUtilities.invokeLater() anzeigen, da dieser Code bereits ausgeführt wurde.Java: Debuggen mit SwingUtilities.invokeLater()

Gibt es Vorschläge, wie Sie einen bestimmten Kontext (nur für Debugging-Zwecke) beim Aufruf von SwingUtilities.invokeLater() festlegen können, damit Sie herausfinden können, was das betreffende UI-Ereignis verursacht hat?

+1

Haben Sie keine akzeptable Lösungen finden?Wirklich interessiert zu wissen, was Sie schließlich getan haben :) – Twister

Antwort

1

Überschreiben Sie die Methode, fügen Sie einen Protokollaufruf hinzu und rufen Sie dann den echten ... Vorbehalt: Sie müssen alle Ihre Aufrufe auf die ursprüngliche Methode ersetzen.

Sie können die ausführbare Datei sogar umbrechen und eine Kontextnummer hinzufügen (z. B. den Zeitstempel des Aufrufs, der später aufgerufen werden soll). Wenn die runnable beginnt es beginnt die Kontextnummer zu drucken

public static void myInvokeLater(final Runnable runnable) { 
    long ts = System.currentTimeMillis(); 
    Log.info("call to invoke later, context :" + ts); 
    Runnable r = new Runnable() { 
      public void run() { 
       Log.info("start runnable of invokeLater with context :" + ts); 
       runnable.run(); 
      } 
     }; 
    SwingUtilities.invokeLater(r); 
} 
+0

Wenn Sie System.out.println (runnable) und System.out.println (this) innerhalb der run-Methode des Runnable, sollten Sie in der Lage sein, sie zu entsprechen :) –

+0

nein System.out.println bitte! –

+0

Und nicht leicht zu deaktivieren. Dort kannst du einfach hinzufügen, wenn (DEBUG) dann die Magie machen, sonst einfach weiterleiten zum echten InvokeLater. – Twister

1

Ich neige dazu, würde „Standard“ swingUtilites # invokeLater Verfahren nach einem fortgeschritteneren zu ersetzen, vielleicht in einigen „localUtilities“ eingebettet, an dem Sie sowohl die Code, der als Argument das Quell-Ereignis oder eine thread-sichere Kopie davon ausführt (ich nehme an, Sie haben ein Quell-Ereignis, unabhängig von seinem Typ).

1

Wenn Sie häufig invokeLater aufrufen, möchten Sie vielleicht das Threading vereinfachen.

invokeLater verwendet effektiv veränderbare Statik und ist daher das reinste Übel. Testen und mehr wird einfacher, wenn Sie vom Aufruf EventQueue.invokeLater zur Verwendung einer Schnittstelle mit invokeLater und isDispatchThread wechseln.

Es ist schade, dass Sie die invokeLater und isDispatchThread, die von Bibliotheken verwendet werden, im Allgemeinen nicht ersetzen können.

+0

Können Sie Ihren Kommentar über "veränderbare Statik" erklären? Ich sehe keinen Grund, warum Sie änderbare Daten über die UI/Nicht-UI-Grenze übergeben müssen. – Anon

+0

@Anon Die Implementierung des statischen 'invokeLater' muss irgendwo entlang der Linie eine [mutable] statische Ereigniswarteschlange auswählen. –

+0

Sie schlagen also vor, die statischen Aufrufe von 'SwingUtilities' durch eine (injizierbare) Instanz zu ersetzen? – Anon

2

Die meisten anderen Antworten hier sind gut, aber ich möchte einen weiteren Vorschlag hinzufügen. Wenn Sie SwingUtilities.invokeLater sehr häufig aufrufen, tun Sie dies wahrscheinlich unnötigerweise, insbesondere wenn der einzige Zweck des Aufrufs darin besteht, sicherzustellen, dass Swing-Änderungen am Ereignisthread vorgenommen werden. Versuchen Sie, diese bei Bedarf:

if (SwingUtilities.isEventDispatchThread()) { 
    myRunnable.run(); 
} else { 
    SwingUtilities.invokeLater(myRunnable); 
} 
+0

Also macht SwingUtilities dies nicht bereits als Teil seiner invokeLater() Implementierung? –

+0

Nein, weil invokeLater nur 'später' ausführt, nachdem alle ausstehenden Ereignisse verarbeitet wurden. Wenn Sie dieses Verhalten brauchen, können Sie diesen Trick natürlich nicht anwenden. invokeAndWait funktioniert möglicherweise auch als eine Alternative. – DJClayworth

1

Gibst du anonym Runnable s invokeLater()?

Wenn ja, würde ich vorschlagen, sie durch nicht-anonyme Klassen zu ersetzen, die ein paar Zeilen Code hinzufügen, aber Ihnen zumindest eine gewisse Rückverfolgbarkeit geben (zB: TableUpdateFromQuery). Dies funktioniert am besten, wenn Sie einen bestimmten Aktualisierungstyp nur von einem Ort in der App aus aufrufen. Es führt Sie auch den Pfad der "Hintergrundaktivitäten", die außerhalb der Benutzeroberfläche getestet werden können.

2

Sie können versuchen, EventQueue zu überschreiben und StackTrace für gepostete Ereignisse zu drucken. Auch im folgenden Beispiel würde jedem gebuchten Ereignis eine eindeutige Nummer zugewiesen werden. Wenn invokeLater würde von anderen invokeLater dann wird der Text postEvent 9 from 7 würde gedruckt im Protokoll

  // Place this code somewhere in the main class to override queue 
     EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
     eventQueue.push(new MyEventQueue()); 

Wo Klasse MyEventQueue könnte wie folgt aussehen genannt werden:

import java.awt.AWTEvent; 
import java.awt.EventQueue; 
import java.awt.event.InvocationEvent; 
import java.util.WeakHashMap; 

public class MyEventQueue extends EventQueue { 

    int currentNumber = 0; 
    WeakHashMap<AWTEvent,Integer> eventIdMap = new WeakHashMap<AWTEvent,Integer>(); 
    AWTEvent currentEvent = null; 

    protected void dispatchEvent(AWTEvent event) { 
     if (event instanceof InvocationEvent) { 
      currentEvent = event; 
     } 
     super.dispatchEvent(event); 
     currentEvent = null; 
    } 

    public void postEvent(AWTEvent event) { 
     if (event instanceof InvocationEvent) { 
      currentNumber = currentNumber + 1; 
      eventIdMap.put(event, currentNumber); 
      System.out.println("postEvent " + currentNumber + " " + 
        (currentEvent != null ? "from " + eventIdMap.get(currentEvent) : "")); 
      for(StackTraceElement element : new RuntimeException().getStackTrace()) { 
       System.out.println("\t" + element); 
      } 
     } 
     super.postEvent(event); 
    } 
} 
+0

Zur Verdeutlichung "verlängert" EventQueue "und überschreibt die gewünschte Methode, z. 'invokeLater()'. Hier ist ein ähnliches Beispiel, http://stackoverflow.com/questions/3158409 – trashgod