Java: отладка с помощью SwingUtilities.invokeLater () - PullRequest
5 голосов
/ 04 января 2011

Я часто использую SwingUtilities.invokeLater().Однако в некоторых случаях это затрудняет отладку: вы не можете увидеть трассировку стека кода, который вызвал SwingUtilities.invokeLater(), потому что этот код уже завершил свое выполнение.

Есть ли какие-либо предложениякак установить некоторый контекст (только для целей отладки) при вызове SwingUtilities.invokeLater(), чтобы вы могли выяснить, что вызвало рассматриваемое событие пользовательского интерфейса?

Ответы [ 6 ]

2 голосов
/ 04 января 2011

Вы можете попробовать переопределить 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 голосов
/ 04 января 2011

Большинство других ответов здесь хороши, но я хочу добавить еще одно предложение.Если вы вызываете SwingUtilities.invokeLater очень часто, вы, вероятно, иногда делаете это излишне, особенно если единственная цель этого вызова - убедиться, что изменения Swing внесены в поток событий.Попробуйте это при необходимости:

if (SwingUtilities.isEventDispatchThread()) {
  myRunnable.run();
} else {
  SwingUtilities.invokeLater(myRunnable);
}
1 голос
/ 04 января 2011

Вы передаете анонимные Runnable с invokeLater()?

Если да, я бы предложил заменить их неанонимными классами, что добавит пару строк кода, но дасту вас хоть какой-то уровень прослеживаемости (например: TableUpdateFromQuery).Это работает лучше всего, если вы вызываете определенный тип обновления только из одного места в приложении.Он также ведет вас по пути «фоновых действий», которые можно протестировать за пределами пользовательского интерфейса.

1 голос
/ 04 января 2011

Если вы часто звоните invokeLater, возможно, вы захотите упростить вашу многопоточность.

invokeLater эффективно использует изменяемую статику и поэтому является чистейшим злом.Тестирование и многое другое станет проще, если вы переключитесь с вызова EventQueue.invokeLater на использование интерфейса с invokeLater и isDispatchThread.

К сожалению, вы вообще не можете заменить invokeLater и isDispatchThread, используемые библиотеками.

1 голос
/ 04 января 2011

Я бы предпочел заменить «стандартный» метод swingUtilites # invokeLater более продвинутым, возможно, встроенным в некоторые «localUtilities», которым вы предоставляете оба кода для выполнения, в качестве аргумента, исходное событие или поток -обезопасная копия (я думаю, у вас есть исходное событие, независимо от его типа).

1 голос
/ 04 января 2011

Переопределите метод, добавьте лог-вызов, а затем вызовите реальный ... Предостережение: вы должны заменить все ваши вызовы на оригинальный метод.

Вы можете даже обернуть исполняемый файл и добавить номер контекста (например, метку времени вызова, чтобы вызвать позже). Когда запускаемый модуль запускается, он начинает печатать номер контекста

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