1

我创建了一个具有多种方法的 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);
        }
    });
}

但这不是很恶心吗?我该怎么做?

4

7 回答 7

3

您可以测试当前是否正在 EDT 上进行呼叫,如果不​​是,则将其Runnable放入SwingUtilites.invokeLater()

public void myMethod() {
       if (SwingUtilities.isEventDispatchThread()) {
            //... your code
       } else {
           SwingUtilities.invokeLater(
                  new Runnable(){
                     public void run() {
                           myMethod();
                     }
                  });
       {
  }                   

如果您在 EDT 上,这将保持方法运行,而不会将其放在事件队列的末尾。

于 2010-06-23T14:54:18.107 回答
2

每当您希望在事件调度线程上执行某些操作时,您应该使用SwingUtilities.invokeLater(Runnable doRun).

从 Javadoc 中提取:

导致 doRun.run() 在 AWT 事件分派线程上异步执行。这将在处理完所有待处理的 AWT 事件后发生。当应用程序线程需要更新 GUI 时,应使用此方法。

于 2010-06-23T14:39:15.870 回答
1

在您需要在 EDT 上运行的所有方法中,您应该将方法主体包装在以下代码中:

SwingUtilities.invokeLater(new Runnable(){public void run(){


    // All code placed in here will be ran asynchronously on the EDT


}});

这将导致方法中的所有内容都在 EDT 上运行。因为您在 EDT 上运行代码,所以您不应该做任何会长时间阻塞的事情。(文件 IO、长时间计算等)否则您的 GUI 将冻结并变得无响应。

invokeLater()文档

于 2010-06-23T14:42:27.977 回答
0

我们实现这一目标的一种方法是使用 AspectJ。样板代码在编译期间注入到所有使用 @OnEDT 注释的方法中。生成的代码很苗条而且非常方便。

不知不觉中,我们不得不为此创建注释、切入点和切面,但这几乎是微不足道的。

如果您有兴趣,这里有一些相关链接:

http://www.eclipse.org/aspectj/

http://www.eclipse.org/ajdt/EclipseCon2006/

http://www.cs.wustl.edu/~mdeters/doc/slides/aspectjtutorial.pdf

于 2010-06-23T20:13:22.577 回答
0

感谢您的所有回复。最后我们(好吧,只是我的朋友不是我!)围绕我们的组件创建了一个包装类。这个包装器接收所有调用,创建一个可运行对象(在 run() 中我们调用实际方法)并将这个可运行对象发送到 invokeLater()。
使用反射库,所有这些似乎都没有那么复杂。起点是动态创建接口实例的 java.lang.reflect.Proxy 类。

于 2010-06-27T03:49:59.347 回答
0

为了避免类的每个方法中存在的“肮脏”,如果你的类实现了一个公共接口,你可以使用 a来执行与你实现方法本身不同Proxy的公共设置。invokeAndWait

  1. 实现一个 Proxy InvocationHandler来处理所有样板代码以检查事件调度线程并调用invokeAndWait
  2. 创建代理的实例并在其上调用方法,而不是直接在您的实现类上。

由于这种方法包含更多的代码,因此仅当您的实现类有大量您希望确保在 EDT 上运行的方法时才适用。

完整示例:

public class ImplClass implements ClassInterface {
    public void a( String paramA, int paramB ) {
        // do something here...
    }
}

public interface ClassInterface {
    void a( String paramA, int paramB );
}

public class MyHandler implements InvocationHandler {
    private ClassInterface implClassInstance;
    public MyHandler( ImplClass implInstance ) {
        this.implClassInstance = implInstance;
    }
    public Object invoke( Object proxy, final Method method, final Object[] args ) throws Throwable {
        if( SwingUtilities.isEventDispatchThread() ) {
            method.invoke( implClassInstance, args );
        }
        else {
            SwingUtilities.invokeAndWait( new Runnable() {
                public void run() {
                    try {
                        method.invoke( implClassInstance, args );
                    }
                    catch( RuntimeException e ) {
                        throw e;
                    }
                    catch( Exception e ) {
                        throw new RuntimeException( e );
                    }
                }
            } );
        }
        return null;
    }
}

以下是使用代理的方法:

// create the proxy like this:
ImplClass implInstance = new ImplClass();
MyHandler handler = new MyHandler(implInstance);
ClassInterface proxy = (ClassInterface) Proxy.newProxyInstance( this.getClass().getClassLoader(), new Class[] { ClassInterface.class }, handler );
// ...
// call the proxy like you would an instance of your implementation class
proxy.a( "paramAValue", 123 );
于 2013-11-13T01:27:32.200 回答
0

使用SwingWorker并在 done() 方法中更新您的 GUI 组件。在 donInBackground() 方法中做后台工作。

于 2010-06-23T15:09:51.807 回答