5

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

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

4

6 回答 6

2

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

        // 将此代码放在主类中的某个位置以覆盖队列
        EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
        eventQueue.push(new MyEventQueue());

MyEventQueue 类可能如下所示:

导入 java.awt.AWTEvent;
导入 java.awt.EventQueue;
导入 java.awt.event.InvocationEvent;
导入 java.util.WeakHashMap;

公共类 MyEventQueue 扩展 EventQueue {

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

    受保护的无效调度事件(AWTEvent事件){
        如果(事件实例InvocationEvent){
            当前事件 = 事件;
        }
        super.dispatchEvent(事件);
        当前事件 = 空;
    }

    公共无效postEvent(AWTEvent事件){
        如果(事件实例InvocationEvent){
            当前编号 = 当前编号 + 1;
            eventIdMap.put(event, currentNumber);
            System.out.println("postEvent " + currentNumber + " " +
                    (currentEvent != null ? "来自 " + eventIdMap.get(currentEvent) : "") );
            for(StackTraceElement 元素:新 RuntimeException().getStackTrace()) {
                System.out.println("\t" + 元素);
            }
        }
        super.postEvent(事件);
    }
}
于 2011-01-04T20:48:55.943 回答
2

这里的大多数其他答案都很好,但我想添加另一个建议。如果您非常频繁地调用 SwingUtilities.invokeLater,那么您可能在某些时候这样做是不必要的,特别是如果调用的唯一目的是确保在事件线程上进行 Swing 更改。在适当的时候试试这个:

if (SwingUtilities.isEventDispatchThread()) {
  myRunnable.run();
} else {
  SwingUtilities.invokeLater(myRunnable);
}
于 2011-01-04T16:41:59.713 回答
1

覆盖该方法,添加一个 Log 调用,然后调用真正的方法......警告:您必须将所有调用替换为原始方法。

您甚至可以包装可运行文件并添加上下文编号(例如稍后调用的调用的时间戳)。当 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);
}
于 2011-01-04T15:33:06.650 回答
1

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

于 2011-01-04T15:36:49.597 回答
1

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

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

于 2011-01-04T16:53:10.397 回答
1

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

invokeLater有效地使用了可变静态,因此是最纯粹的邪恶。如果您从调用切换EventQueue.invokeLater到使用带有invokeLater和的接口,测试和更多操作将变得更加容易isDispatchThread

不幸的是,一般来说,您不能替换库所使用的invokeLaterisDispatchThread

于 2011-01-04T16:19:55.103 回答