Java:使用SwingUtilities.invokeLater()进行调试

时间:2011-01-04 15:29:34

标签: java debugging swing event-dispatch-thread

我经常使用SwingUtilities.invokeLater()。但是,这样做会使某些情况下的调试变得困难:您无法看到调用SwingUtilities.invokeLater()的代码的堆栈跟踪,因为该代码已经结束了它的执行。

在调用SwingUtilities.invokeLater()时,是否有关于如何设置某种上下文(仅用于调试目的)的建议,以便您可以找出导致相关UI事件的原因?

6 个答案:

答案 0 :(得分:2)

这里的大多数其他答案都很好,但我想补充一点建议。如果你经常调用SwingUtilities.invokeLater,你可能在某些时候不必要地执行它,特别是如果调用的唯一目的是确保在事件线程上进行Swing更改。请在适当的时候尝试:

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

答案 1 :(得分:2)

您可以尝试覆盖EventQueue并打印已发布事件的堆栈跟踪。同样在下面的示例中,将为每个发布的事件分配唯一编号。如果从其他invokeLater调用invokeLater,则会在日志中打印文本postEvent 9 from 7

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

MyEventQueue类可能如下所示:

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);
    }
}

答案 2 :(得分:1)

覆盖方法,添加日志调用,然后调用真实的... 警告:您必须将所有调用替换为原始方法。

您甚至可以包装runnable并添加上下文编号(例如稍后调用的调用的时间戳)。当runnable启动时,它开始打印上下文编号

 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);
}

答案 3 :(得分:1)

我倾向于用更高级的方法替换“标准”swingUtilites#invokeLater方法,也可以嵌入到某些“localUtilities”中,您可以将代码作为参数执行,以源事件或线程执行 - 它的安全副本(我想你有一个源事件,无论它的类型是什么)。

答案 4 :(得分:1)

如果您经常致电invokeLater,可能需要考虑简化线程。

invokeLater实际上是使用可变静力学,因此是最纯粹的邪恶。如果您从调用EventQueue.invokeLater切换到使用invokeLaterisDispatchThread的界面,测试及其他内容将变得更加轻松。

遗憾的是,您通常无法替换图书馆使用的invokeLaterisDispatchThread

答案 5 :(得分:1)

您是否将匿名Runnable传递给invokeLater()

如果是,我建议用非匿名类替换它们,这将添加几行代码,但会给你至少一定程度的可追溯性(例如:TableUpdateFromQuery)。如果您仅从应用程序中的一个位置调用特定类型的更新,则此方法效果最佳。它还引导您走上“后台活动”的道路,可以在UI之外进行测试。