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

发布于 2024-10-10 00:09:44 字数 403 浏览 3 评论 0原文

我使用 SwingUtilities.invokeLater() 频繁。然而,这样做在某些情况下会导致调试变得困难:您看不到调用 SwingUtilities.invokeLater() 的代码的堆栈跟踪,因为该代码已经结束了执行。

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

I use SwingUtilities.invokeLater() frequently. Doing so, however, makes it difficult to debug in certain cases: you can't see a stack trace of the code that called SwingUtilities.invokeLater(), because that code has already ended its execution.

Are there any suggestions for how to set some sort of context (for debugging purposes only) when calling SwingUtilities.invokeLater(), so that you can figure out what caused the UI event in question?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

旧人哭 2024-10-17 00:09:44

您可以尝试覆盖 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);
    }
}

You can try to override EventQueue and print stacktrace for posted events. Also in the example below a unique number would be assigned to each posted event. When invokeLater would be called from other invokeLater then the text postEvent 9 from 7 would be printed in the log

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

Where class MyEventQueue might look like this:

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);
    }
}
如日中天 2024-10-17 00:09:44

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

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

Most of the other answers here are good, but I want to add another suggestion. If you are calling SwingUtilities.invokeLater very frequently, you are probably doing it unnecessarily some of the time, especially if the sole purpose of the call is to ensure that Swing changes are made on the event thread. Try this when appropriate:

if (SwingUtilities.isEventDispatchThread()) {
  myRunnable.run();
} else {
  SwingUtilities.invokeLater(myRunnable);
}
↘紸啶 2024-10-17 00:09:44

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

I would tend to replace "standard" swingUtilites#invokeLater method by a more advanced one, maybe embedded in some "localUtilities", to which you give both the code to execute with, as an argument, the source event or a thread-safe copy of it (I guess you have a source event, whatever its type is).

鲜肉鲜肉永远不皱 2024-10-17 00:09:44

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

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

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

Override the method, add a Log call and then call the real one...
Caveat : you have to replace all your calls to the original method.

You can even wrap the runnable and add a context number (for example the timestamp of the call to invoke later). When the runnable starts it began to print the context number

 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);
}
晨与橙与城 2024-10-17 00:09:44

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

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

Are you passing anonymous Runnables to invokeLater()?

If yes, I'd suggest replacing them with non-anonymous classes, which will add a couple of lines of code, but will give you at least some level of traceability (eg: TableUpdateFromQuery). This works best if you invoke a particular type of update from only one place in the app. It also leads you down the path of "background activities," which can be tested outside of the UI.

嘿咻 2024-10-17 00:09:44

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

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

不幸的是,通常您无法替换库使用的 invokeLaterisDispatchThread

If you are calling invokeLater frequently, you might want to consider simplifying your threading.

invokeLater is effectively using mutable statics and is therefore purest evil. Testing and more will become easier if you switch from calling EventQueue.invokeLater to using an interface with invokeLater and isDispatchThread.

It is unfortunate that you can't, in general, replace the invokeLater and isDispatchThread used by libraries.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文