我们都知道应该从事件分发线程执行所有与 GUI 相关的任务,否则可能会引入奇怪的错误。我尽力记住这个规则,但最近我注意到了一些违例。
有没有一种方法可以确定所有违反此规则的情况,以便进行修复?我看到有一个相关的 FindBugs 规则(在此处),但它似乎不能捕获所有情况。即使只是在违规发生时抛出异常,也很好,这样我就可以修复它(或者在用户遇到相关问题时捕获异常并记录警告)。
人们通常采用什么方法处理这个问题?
有没有一种方法可以确定所有违反此规则的情况,以便进行修复?我看到有一个相关的 FindBugs 规则(在此处),但它似乎不能捕获所有情况。即使只是在违规发生时抛出异常,也很好,这样我就可以修复它(或者在用户遇到相关问题时捕获异常并记录警告)。
人们通常采用什么方法处理这个问题?
import javax.swing.JComponent;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import org.apache.log4j.Logger;
import sun.awt.AppContext;
public class DetectEdtViolationRepaintManager extends RepaintManager {
private static final Logger LOGGER = Logger.getLogger(
DetectEdtViolationRepaintManager.class);
/**
* Used to ensure we only print a stack trace once per abusing thread. May
* be null if the option is disabled.
*/
private ThreadLocal alreadyWarnedLocal;
/**
* Installs a new instance of DetectEdtViolationRepaintManager which does not
* warn repeatedly, as the current repaint manager.
*/
public static void install() {
install(false);
}
/**
* Installs a new instance of DetectEdtViolationRepaintManager as the current
* repaint manager.
* @param warnRepeatedly whether multiple warnings should be logged for each
* violating thread
*/
public static void install(boolean warnRepeatedly) {
AppContext.getAppContext().put(RepaintManager.class,
new DetectEdtViolationRepaintManager(warnRepeatedly));
LOGGER.info("Installed new DetectEdtViolationRepaintManager");
}
/**
* Creates a new instance of DetectEdtViolationRepaintManager.
* @param warnRepeatedly whether multiple warnings should be logged for each
* violating thread
*/
private DetectEdtViolationRepaintManager(boolean warnRepeatedly) {
if (!warnRepeatedly) {
this.alreadyWarnedLocal = new ThreadLocal();
}
}
/**
* {@inheritDoc}
*/
public synchronized void addInvalidComponent(JComponent component) {
checkThreadViolations();
super.addInvalidComponent(component);
}
/**
* {@inheritDoc}
*/
public synchronized void addDirtyRegion(JComponent component, int x, int y,
int w, int h) {
checkThreadViolations();
super.addDirtyRegion(component, x, y, w, h);
}
/**
* Checks if the calling thread is called in the event dispatch thread.
* If not an exception will be printed to the console.
*/
private void checkThreadViolations() {
if (alreadyWarnedLocal != null && Boolean.TRUE.equals(alreadyWarnedLocal.get())) {
return;
}
if (!SwingUtilities.isEventDispatchThread()) {
if (alreadyWarnedLocal != null) {
alreadyWarnedLocal.set(Boolean.TRUE);
}
LOGGER.warn("painting on non-EDT thread", new Exception());
}
}
}
我只是尽量小心谨慎。但是你可以安装代码来测试一个给定的代码是否在调度线程中执行,使用SwingUtilities.isEventDispatchThread(),如果它不是(或者如果它是),你可以根据自己的喜好进行操作。
Substance Look and Feel包含一个自动的运行时EDT违规检查器。它可以帮助在测试时捕获EDT违规情况。当发现违规时,它会抛出IllegalStateException异常。这对于测试很有用。
Fest的Swing模块还包括一个EDT违规检查器,您可以安装它,当它检测到违规时会抛出异常。