Как я могу запустить все методы класса в потоке диспетчеризации событий? - PullRequest
1 голос
/ 23 июня 2010

Я создал компонент Swing, который имеет несколько методов.
Теперь я хочу, чтобы все методы этого класса запускались в потоке диспетчеризации событий (EDT), а вызывающие - в рабочих потоках.Единственное решение, которое я сейчас думаю, это:
для каждого метода

public void a(params)  

в этом классе, я должен переименовать его в

private void aOnEDT(params)  

и добавить другой метод

 
public void a(params){
    SwingUtilities.invokeAndWait(new Runnable(){
        public void run() {
            aOnEDT(params);
        }
    });
}

Но разве это не противно?

Как я могу это сделать?

Надеюсь, кто-нибудь поможет мне в этом.

Спасибо

Ответы [ 7 ]

2 голосов
/ 23 июня 2010

Вы можете проверить, выполняется ли вызов в данный момент на EDT или нет, и перетащить Runnable в SwingUtilites.invokeLater(), если это не так:

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

Это сохранит метод, есливы находитесь на EDT, не помещая его в конец очереди событий.

1 голос
/ 23 июня 2010

Во всех методах, которые вам нужно запустить в EDT, вы должны заключить тело метода в следующий код:

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


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


}});

Это приведет к запуску всех элементов метода на EDT. Поскольку вы выполняете код на EDT, вы не должны делать ничего, что будет блокировать в течение длительного времени. (Файл ввода-вывода, длинные вычисления и т. Д.) В противном случае ваш графический интерфейс будет зависать и перестать отвечать.

invokeLater() Javadocs

1 голос
/ 23 июня 2010

Всякий раз, когда вы хотите что-то выполнить в потоке диспетчеризации событий, вы должны использовать SwingUtilities.invokeLater(Runnable doRun).

Извлечение из Javadoc:

Причины doRun.run () должен выполняться асинхронно в потоке диспетчеризации событий AWT.Это произойдет после обработки всех ожидающих событий AWT.Этот метод следует использовать, когда поток приложения должен обновить графический интерфейс.

0 голосов
/ 13 ноября 2013

Чтобы избежать "злобности" внутри каждого метода вашего класса, если ваш класс реализует открытый интерфейс, вы можете использовать Proxy для выполнения общей настройки invokeAndWait отдельно от где вы реализуете сами методы.

  1. Реализация прокси 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 );
0 голосов
/ 27 июня 2010

Спасибо за все ваши ответы.Наконец мы (ну просто мой друг, а не я!) Создали класс-оболочку для нашего компонента.Эта оболочка получает все вызовы, создает runnable (внутри run (), который мы вызываем фактическим методом), и отправляет этот runnable в invokeLater ().
Все это не так сложно, используя библиотеку отражений.Отправной точкой является класс java.lang.reflect.Proxy, который динамически создает экземпляр интерфейса.

0 голосов
/ 24 июня 2010

Один из способов, которым мы достигли, это использование AspectJ. Стандартный шаблон внедряется во время компиляции во все методы, помеченные @OnEDT. Результирующий код тонкий и очень удобный.

Очевидно, что для этого нам пришлось создать аннотацию, точечный вырез и аспект, но это было почти тривиально.

Вот несколько связанных ссылок, если вы заинтересованы:

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

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

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

0 голосов
/ 23 июня 2010

Используйте SwingWorker и обновите GUI-компоненты с помощью метода done (). Выполните фоновую работу в методе donInBackground ().

...