如何从 JButton ActionListener 调用重算法

How to call a heavy algorithm from JButton ActionListener

我正在编写一个既充当服务器又充当客户端的 Java 程序。忽略不相关的位,它有三个 类:Main、Server 和 Client。 Main 只是设置一个菜单并包含 main 方法。 Server和Client分别保存了服务器端和客户端的算法。

我想做的是根据按下的按钮从服务器和客户端 类 及其 GUI 调用算法。调用服务器的代码目前如下所示:

serverButton = new JButton();
serverButton.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        server.showGUI();
        server.run();
    }
});

问题是 server.run() 连续运行了很长一段时间,而且工作量很大。这会破坏 GUI,据我了解,这是因为我正在从 EDT 调用该方法。

如何从主线程调用这个方法?我是否需要创建一个 SwingWorker 并将其留在那里直到 server.run() 结束?

确保服务器对象引用是最终的,然后在 actionPerformed 方法的新线程中调用该方法。

Runnable task = () -> {server.run();};
Thread thread = new Thread(task);
thread.start();

这取决于您的要求,如果您希望用户在服务器 returns 之前不想做任何事情,最好在 Busyindicator 中进行,例如 :

public void actionPerformed( ActionEvent e )
{

    BusyIndicator.showWhile(Display.getCurrent(), new Runnable() 
    {
        @Override
        public void run() 
        {
           server.run();
        }
    });
}

这将在服务器 运行 运行时向用户显示一个沙漏,用户被阻止使用 UI。

如果您希望 UI 响应,您需要在单独的线程中调用 server.run()

MyThread t = new MyThread()
{
   public void run()
   {
      server.run();
   }
}
t.start();

最好向线程添加一个侦听器以通知服务器响应完成,这样 UI 就可以做它的事情了。

t.addListener( new MyThreadListener()
{
   public void serverDone()
   {
       Display.getDefault().asyncExec( new Runnable()
       {
           public void run()
           {
           }
       });
  }
});

请注意,这不是线程侦听器的完整代码,仅供参考。

How can I call this method from the main thread?

这是 Swing 中通常的做法。

public class WhatEverServer {

    private UserInterface userInterface;
    [...]

    private static void createAndShowGUI() {

      if( GraphicsEnvironment.isHeadless() )
        logger.log( Level.FATAL, "This system seems to be 'headless'. Aborting now." ); 
      else {
        userInterface = UserInterface.getInstance();
        userInterface.createAndShowUI();
      }
    }

    public static void main( String[] args ) {

        // schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater( new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}


public class UserInterface {

    ...
    public void createAndShowUI() {

      // make sure we have nice window decorations.
      JFrame.setDefaultLookAndFeelDecorated(true);

      UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );

      // create and set up the window.
      JFrame frame = new JFrame( "Whatever Server" );
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      // set UI components, i.e

      // set main menu bar
      frame.setJMenuBar( this.mainMenuBar );

      // set layout
      frame.getContentPane().setLayout( new BorderLayout() );

      // add UI components

      // display the window.
      frame.pack();
      frame.setVisible(true);
    }
}

This bugs out the GUI, which from my understanding is because I'm calling the method from the EDT.

是的,由于操作是由事件触发的,因此 actionPerformed() 由 EDT(或在 EDT 上)调用。我不知道你在 server.run() 中做什么,但我想这不应该出现在 EDT 上。

Do I need to create a SwingWorker and leave it there until the end of server.run()?

在这种情况下,我会使用 SwingWorker 或 SwingUtilities。您可以使用两个线程以这种方式编写一个 ActionHandler,一个用于执行某些 'heavy lifting',一个用于设置 UI :

public void actionPerformed(ActionEvent e) {

  new Thread(new Runnable {
    public void run() {
      ...
      // do some 'heavy lifting' here ...

      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          server.setupUI();
        }  
      )
      ...
      // or do some 'heavy lifting' here
    }); 
  }
}