我创建了一个具有几种方法的Swing组件。我希望此类的所有方法都在Swing事件调度线程(EDT)上运行,而调用方在Worker线程上。我目前唯一想到的解决方案是:
对于此类中的每个方法:
public void a(params)
我应该将其重命名为:
private void aOnEDT(params)
并添加另一种方法:
public void a(params) {
SwingUtilities.invokeAndWait(new Runnable(){
public void run() {
aOnEDT(params);
}
});
}
但是不是很讨厌吗?我应该怎么做?
您可以测试当前是否正在EDT上拨打电话,如果没有,将Runnable
放到SwingUtilites.invokeLater()
中:
public void myMethod() {
if (SwingUtilities.isEventDispatchThread()) {
//... your code
} else {
SwingUtilities.invokeLater(
new Runnable(){
public void run() {
myMethod();
}
});
{
}
如果您在EDT上,则该方法将保持运行状态,而不会将其置于事件队列的末尾。
[只要您想在事件调度线程上执行某些操作,就应该使用SwingUtilities.invokeLater(Runnable doRun)
。
摘自Javadoc:
导致doRun.run()被执行在AWT事件上异步调度线程。这会发生在所有未决的AWT事件发生之后处理。应该使用这种方法当应用程序线程需要更新GUI。
在您需要在EDT上运行的所有方法中,应将方法主体包装在以下代码中:
SwingUtilities.invokeLater(Runnable doRun)
这将使方法中的所有内容都在EDT上运行。因为您正在EDT上运行代码,所以您不应做任何会长时间阻塞的事情。 (文件IO,冗长的计算等)否则,您的GUI将冻结并变得无响应。
SwingUtilities.invokeLater(new Runnable(){public void run(){
// All code placed in here will be ran asynchronously on the EDT
}});
使用invokeLater()
javadocs,并在done()方法中更新您的GUI组件。在donInBackground()方法中进行后台工作。
我们完成此工作的一种方法是使用AspectJ。在编译过程中将样板代码注入到所有用@OnEDT注释的方法中。结果代码很苗条,非常方便。
显然,我们必须为此创建注释,切入点和方面,但这几乎是微不足道的。
如果您感兴趣,这里有几个相关链接:
invokeLater()
感谢您的所有回复。最后,我们(好吧,只是我的朋友而不是我!)围绕我们的组件创建了一个包装器类。该包装器接收所有调用,创建一个可运行对象(在我们称为实际方法的run()内部),然后将此可运行对象发送给invokeLater()。使用反射库,所有这些似乎并不那么复杂。起点是java.lang.reflect.Proxy类,该类动态创建接口的实例。
为了避免类的每个方法中都存在“麻烦”,如果您的类实现了公共接口,则可以使用http://www.eclipse.org/ajdt/EclipseCon2006/来执行与实现方法本身分开的通用http://www.cs.wustl.edu/~mdeters/doc/slides/aspectjtutorial.pdf设置。
Proxy
,该代理将处理所有样板代码以检查事件分配线程并调用Proxy
由于此方法预先包含了更多的代码,因此,仅当您的实现类具有大量要确保在EDT上运行的方法时,它才真正合适。
完整示例:
invokeAndWait
这是使用代理的方式:
InvocationHandler