簡體   English   中英

如何從JButton ActionListener調用繁重的算法

[英]How to call a heavy algorithm from JButton ActionListener

我正在編寫一個既充當服務器又充當客戶端的Java程序。 忽略不相關的位,它具有三個類別:Main,Server和Client。 Main只是設置一個菜單並包含main方法。 服務器和客戶端分別持有服務器和客戶端的算法。

我想要做的是根據所按下的按鈕從服務器和客戶端類及其GUI調用算法。 當前調用服務器的代碼如下:

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

問題是server.run()連續運行了很長一段時間,而且繁重。 這使GUI出錯,據我了解,這是因為我正在從EDT調用該方法。

如何從主線程調用此方法? 我是否需要創建一個SwingWorker並將其保留到server.run()結束?

如何從主線程調用此方法?

這是通常在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);
    }
}

這使GUI出錯,據我了解,這是因為我正在從EDT調用該方法。

是的,由於操作是由事件觸發的,因此EDT(或在EDT上)會調用actionPerformed()。 我不知道您在server.run()中做什么,但是我想這不應該在EDT上結束。

我是否需要創建一個SwingWorker並將其保留到server.run()結束?

在這種情況下,我將使用SwingWorker或SwingUtilities。 您可以以這種方式編寫ActionHandler,使用兩個線程,一個用於執行某些“繁重的工作”,一個用於設置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
    }); 
  }
}

確保服務器對象引用是最終的,然后在actionPerformed方法的新線程中調用該方法。

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

這取決於您的要求,如果您希望用戶在服務器返回之前不希望做任何事情,最好在Busyindicator中執行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()
           {
           }
       });
  }
});

請注意,出於主意考慮,這不是用於線程偵聽器的完整代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM