2016-11-12 45 views
1

我正在將文件導入到項目中,一旦用戶單擊選擇此文件,我會執行一些操作,其中包括通過命令行執行的操作。等待後臺進程以不同類別的SwingWorker結束

我已經使用這個方法來完成它:

p = Runtime.getRuntime().exec(command, null, new File(directory)); 
p.waitFor(); 

由於這一過程可能需要一些時間,我決定把顯示新幀當前進程的所有輸出一個進度條彈出。我遵循@trashgod的this代碼適應了我的項目。爲了這個問題的目的,讓我們說我完全按照它的樣子。

我只想讓這個窗口在命令運行時出現,然後關閉它自己。我可以通過從override override方法中放置框架來完成此操作。

我的問題是,我從其他類創建這個類,而這是正在執行它繼續與代碼。例如:

method1(); 
method2(); 
new SwingWorkerExample().displayGUI(); //Process continues in background 
method3(); 

這裏我的方法3()在SwingWorkerExample類的過程完成之前正在執行。我怎麼能解決這個問題?

我已經試圖把在displayGUI方法本月底:

while(!backgroundTask.isDone()){ 
//do nothing 
} 

但它使整個框架無法正常工作或可能凍結它。 我也試着寫了使p在backgrandtask類的全局變量後的方法:

public Process getProcess(){ 
return p; 
} 

然後從displaGUI調用()以下:

backgroundTask.getProcess().waitFor(); 

但它返回一個空指針異常。

我還能做些什麼來等待顯示命令窗口的輸出時結束進程?

謝謝。 更新: 下面是鏈接代碼的副本,其中包含一些更改以顯示我的意思。我不希望打印的第二個文本,直到處理完畢:

package mcve; 
import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.event.*; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import javax.swing.*; 

/** 
* @se https://stackoverflow.com/a/20603012/230513 
* @see https://stackoverflow.com/a/17763395/230513 
* @seehttps://stackoverflow.com/questions/20600721/ 
*/ 
public class SwingWorkerExample { 

private JFrame frame; 
private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
private final JTextArea textArea = new JTextArea(20, 20); 
private JButton startButton = new JButton("Start"); 
private JButton stopButton = new JButton("Stop"); 
private JProgressBar bar = new JProgressBar(); 
private BackgroundTask backgroundTask; 
private final ActionListener buttonActions = new ActionListener() { 
    @Override 
    public void actionPerformed(ActionEvent ae) { 
     JButton source = (JButton) ae.getSource(); 
     if (source == startButton) { 
      textArea.setText(null); 
      startButton.setEnabled(false); 
      stopButton.setEnabled(true); 
      backgroundTask = new BackgroundTask(); 
      backgroundTask.execute(); 
      bar.setIndeterminate(true); 
     } else if (source == stopButton) { 
      backgroundTask.cancel(true); 
      backgroundTask.done(); 
     } 
    } 
}; 

private void displayGUI() { 
    frame = new JFrame("Swing Worker Example"); 
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 

    JPanel panel = new JPanel(); 
    panel.setBorder(
     BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
    panel.setLayout(new BorderLayout(5, 5)); 

    JScrollPane sp = new JScrollPane(); 
    sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
    sp.setViewportView(textArea); 

    startButton.addActionListener(buttonActions); 
    stopButton.setEnabled(false); 
    stopButton.addActionListener(buttonActions); 
    JPanel buttonPanel = new JPanel(); 
    buttonPanel.add(startButton); 
    buttonPanel.add(stopButton); 
    buttonPanel.add(bar); 

    panel.add(statusLabel, BorderLayout.PAGE_START); 
    panel.add(sp, BorderLayout.CENTER); 
    panel.add(buttonPanel, BorderLayout.PAGE_END); 

    frame.setContentPane(panel); 
    frame.pack(); 
    frame.setLocationByPlatform(true); 
    frame.setVisible(true); 
} 

private class BackgroundTask extends SwingWorker<Integer, String> { 

    private int status; 

    public BackgroundTask() { 
     statusLabel.setText((this.getState()).toString()); 
    } 

    @Override 
    protected Integer doInBackground() { 
     try { 
      ProcessBuilder pb = new ProcessBuilder("ping", "-c", "5","google.com");//change -c for -n in windows 
      pb.redirectErrorStream(true); 
      Process p = pb.start(); 
      String s; 
      BufferedReader stdout = new BufferedReader(
       new InputStreamReader(p.getInputStream())); 
      while ((s = stdout.readLine()) != null && !isCancelled()) { 
       publish(s); 
      } 
      if (!isCancelled()) { 
       status = p.waitFor(); 
      } 
      p.getInputStream().close(); 
      p.getOutputStream().close(); 
      p.getErrorStream().close(); 
      p.destroy(); 
     } catch (IOException | InterruptedException ex) { 
      ex.printStackTrace(System.err); 
     } 
     return status; 
    } 

    @Override 
    protected void process(java.util.List<String> messages) { 
     statusLabel.setText((this.getState()).toString()); 
     for (String message : messages) { 
      textArea.append(message + "\n"); 
     } 
    } 

    @Override 
    protected void done() { 
     statusLabel.setText((this.getState()).toString() + " " + status); 
     stopButton.setEnabled(false); 
     startButton.setEnabled(true); 
     bar.setIndeterminate(false); 
     frame.dispose(); 
    } 

} 

public static void main(String[] args) { 
    EventQueue.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Here I start"); 
      new SwingWorkerExample().displayGUI(); 
      System.out.println("I'll show up when the SwingWorkerExample has finnished"); 
     } 
    }); 
} 

UPDATE2:我也試圖把在main方法(displayGUI)的過程,併發送至一個參考過程,但它不工作,窗口只是凍結,並沒有顯示出來:

frame.setVisible(true); 
    bar.setIndeterminate(true); 
    try { 
     ProcessBuilder pb = new ProcessBuilder("ping", "-c", "20", "google.com"); 
     pb.redirectErrorStream(true); 
     Process p; 
     p = pb.start(); 
     backgroundTask = new BackgroundTask(p); 
     backgroundTask.execute(); 
     p.waitFor(); 
    } catch (IOException | InterruptedException ex) { 
     ex.printStackTrace(System.err); 
    } 

更新3:繼Eals答案我有這樣結束了,仍然沒有按的@Hovercraft全」 t如預期的那樣工作,因爲字符串在過程結束之前出現。也許我的理解是錯誤的或者它只是沒有在這方面的工作:

package mcve; 

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.event.*; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.ArrayList; 
import java.util.List; 
import javax.swing.*; 
import static javax.swing.Action.MNEMONIC_KEY; 

public class SwingWorkerExample { 

private JFrame frame; 
private ExecuteFrame backgroundTask; 


private class properyListener implements PropertyChangeListener { 
    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     // the above percolates from the SwingWorker to the containing 
     // JPanel 
     if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
      int processState = backgroundTask.getProcessStatus(); 
      System.out.println("Process State: " + processState + "\n"); 
     } 
    } 
} 
private void displayGUI() { 
    frame = new JFrame("Swing Worker Example"); 
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 

    backgroundTask = new ExecuteFrame(); 
    frame.add(backgroundTask); 
    backgroundTask.addPropertyChangeListener(new properyListener()); 

    frame.pack(); 
    frame.setLocationByPlatform(true); 
    frame.setVisible(true); 

    backgroundTask.startProcess(); 
    } 
public static void main(String[] args) { 
    EventQueue.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Here I start"); 
      SwingUtilities.invokeLater(() -> new SwingWorkerExample().displayGUI()); 
      System.out.println("I'll show up when the SwingWorkerExample has finnished"); 
     } 
    }); 
} 
} 

@SuppressWarnings("serial") 
class ExecuteFrame extends JPanel { 

private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
private final JTextArea textArea = new JTextArea(20, 30); 
private StartAction startAction = new StartAction("Start", KeyEvent.VK_S); 
private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T); 
private JProgressBar bar = new JProgressBar(); 
private BackgroundTask backgroundTask; 
private int processStatus; 

public ExecuteFrame() { 
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
    setLayout(new BorderLayout(5, 5)); 

    textArea.setFocusable(false); 
    JScrollPane sp = new JScrollPane(); 
    sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
    sp.setViewportView(textArea); 

    stopAction.setEnabled(false); 
    JPanel buttonPanel = new JPanel(); 
    buttonPanel.add(new JButton(startAction)); 
    buttonPanel.add(new JButton(stopAction)); 
    buttonPanel.add(bar); 

    add(statusLabel, BorderLayout.PAGE_START); 
    add(sp, BorderLayout.CENTER); 
    add(buttonPanel, BorderLayout.PAGE_END); 
} 

public void startProcess() { 
    if (backgroundTask != null && !backgroundTask.isDone()) { 
     return; // background task not yet done 
    } 
    textArea.setText(""); 
    startAction.setEnabled(false); 
    stopAction.setEnabled(true); 
    backgroundTask = new BackgroundTask(); 
    backgroundTask.addPropertyChangeListener(new BGTaskListener()); 
    backgroundTask.execute(); 
    bar.setIndeterminate(true); 
} 

public void cancelProcess() { 
    if (backgroundTask != null && !backgroundTask.isDone()) { 
     backgroundTask.cancel(true); 
    } 
} 

public void processStopped() { 
    statusLabel.setText((backgroundTask.getState()).toString() + " " 
      + processStatus); 
    stopAction.setEnabled(false); 
    startAction.setEnabled(true); 
    bar.setIndeterminate(false); 

    // Window thisWindow = SwingUtilities.getWindowAncestor(textArea); 
    // thisWindow.dispose(); 
} 

public int getProcessStatus() { 
    return processStatus; 
} 

public class BGTaskListener implements PropertyChangeListener { 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
      processStopped(); 
     } 

     // percolate this listener up to the main JPanel's Prop Chng 
     // Listener 
     ExecuteFrame.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), 
       evt.getNewValue()); 
    } 
} 

private class BackgroundTask extends SwingWorker<Integer, String> { 

    @Override 
    protected Integer doInBackground() throws Exception { 
     List<String> list = new ArrayList<>(); 
     list.add("ping"); 
     String name = System.getProperty("os.name"); 
     if (name.startsWith("Win")) { 
      list.add("-n"); 
     } else { 
      list.add("-c"); 
     } 
     list.add("5"); 
     list.add("google.com"); 
     try { 
      ProcessBuilder pb = new ProcessBuilder(list); 
      pb.redirectErrorStream(true); 
      Process p = pb.start(); 
      String s; 
      BufferedReader stdout = new BufferedReader(
        new InputStreamReader(p.getInputStream())); 
      while ((s = stdout.readLine()) != null && !isCancelled()) { 
       publish(s); 
      } 
      if (!isCancelled()) { 
       processStatus = p.waitFor(); 
      } 
      p.getInputStream().close(); 
      p.getOutputStream().close(); 
      p.getErrorStream().close(); 
      p.destroy(); 
     } catch (IOException | InterruptedException ex) { 
      ex.printStackTrace(System.err); 
     } 
     return processStatus; 
    } 

    @Override 
    protected void process(java.util.List<String> messages) { 
     statusLabel.setText((this.getState()).toString()); 
     for (String message : messages) { 
      textArea.append(message + "\n"); 
     } 
    } 

} 

private class StartAction extends AbstractAction { 

    public StartAction(String name, int mnemonic) { 
     super(name); 
     putValue(MNEMONIC_KEY, mnemonic); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     startProcess(); 
    } 
} 

private class StopAction extends AbstractAction { 

    public StopAction(String name, int mnemonic) { 
     super(name); 
     putValue(MNEMONIC_KEY, mnemonic); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     cancelProcess(); 
    } 
} 
} 

輸出是:

Here I start 
I'll show up when the SwingWorkerExample has finnished 
Process State: 0 

相反的:

Here I start 
Process State: 0 
I'll show up when the SwingWorkerExample has finnished 

回答

1

最好的解決方案是將PropertyChangeListener添加到您的SwingWorker及其回調方法中,檢查結束的過程。這將允許您的Swing事件線程運行,並在工作完成時通知您Swing事件線程。您甚至可以監聽對worker的progress屬性的更改,並從此偵聽器中更改JProgressBar。例如(代碼未測試):

private class TaskListener implements PropertyChangeListener { 
    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     SwingWorker task = (SwingWorker) evt.getSource(); 

     // here we can listen for changes to the worker's progress property 
     // if we desire 
     if ("progress".equals(evt.getPropertyName())) { 
      int progress = task.getProgress(); 
      progressBar.setValue(progress); 
     } 

     // listen for the worker to be done 
     if (SwingWorker.StateValue.DONE == evt.getNewValue()) { 
      // always need to know when the SW is done 
      // so we can call get() and trap exceptions 
      try { 
       task.get(); // do something with the result returned? 

       // !! here do things that you want done when the SwingWorker has completed *** 

      } catch (InterruptedException | ExecutionException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

別處......

MySwingWorker myWorker = new MySwingWorker(); 
myWorker.addPropertyChangeListener(new TaskListener()); 
myWorker.execute(); 

關於:

別的我能做什麼等待進程結束,而顯示輸出的命令窗口?

獲取並顯示命令窗口輸出是一個完全不同的問題,並且需要從運行的後臺進程獲取InputStream。


編輯我玩這個,代碼顯示兩個窗口,第二個做擺動工人的工作,對上是第二部分的JPanel的屬性更改第一監聽,但它不是很乾淨的代碼,因爲它不具備的憂慮良好的MVC分離:

import java.awt.BorderLayout; 
import java.awt.Dialog.ModalityType; 
import java.awt.Window; 
import java.awt.event.ActionEvent; 
import java.awt.event.KeyEvent; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.ArrayList; 
import java.util.List; 
import javax.swing.*; 

public class SwingWorkerExample2 { 

    private static void createAndShowGui() { 
     SWEx2MainPanel mainPanel = new SWEx2MainPanel(); 

     JFrame frame = new JFrame("SwingWorker Example"); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.getContentPane().add(mainPanel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> createAndShowGui()); 
    } 
} 

@SuppressWarnings("serial") 
class SWEx2MainPanel extends JPanel { 
    public static final ModalityType DLG_MODALITY_TYPE = ModalityType.MODELESS; 
    private JTextArea statusArea = new JTextArea(15, 30); 
    private JDialog workerDialog; 
    private SWEx2WorkerPanel workerPanel = new SWEx2WorkerPanel(); 

    public SWEx2MainPanel() { 
     JPanel buttonPanel = new JPanel(); 
     buttonPanel.add(new JButton(new ShowWorkerDialogAction())); 
     statusArea.setFocusable(false); 
     JScrollPane scrollPane = new JScrollPane(statusArea); 
     scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 

     setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     setLayout(new BorderLayout(5, 5)); 
     add(scrollPane); 
     add(buttonPanel, BorderLayout.PAGE_END); 

     workerPanel.addPropertyChangeListener(new WorkerPanelListener()); 
    } 

    private class WorkerPanelListener implements PropertyChangeListener { 
     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      // the above percolates from the SwingWorker to the containing 
      // JPanel 
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
       int processState = workerPanel.getProcessStatus(); 
       statusArea.append("Process State: " + processState + "\n"); 
      } 
     } 
    } 

    private class ShowWorkerDialogAction extends AbstractAction { 
     public ShowWorkerDialogAction() { 
      super("Show Worker"); 
      putValue(MNEMONIC_KEY, KeyEvent.VK_S); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      if (workerDialog == null) { 
       Window window = SwingUtilities.getWindowAncestor(statusArea); 
       workerDialog = new JDialog(window, "Worker Dialog", DLG_MODALITY_TYPE); 
       workerDialog.add(workerPanel); 
       workerDialog.pack(); 
       workerDialog.setLocationByPlatform(true); 
      } else if (workerDialog.isVisible()) { 
       // dialog is still visible -- do nothing 
       return; 
      } 
      workerDialog.setVisible(true); 
     } 
    } 
} 

// class that holds the JPanel that does background communication 
@SuppressWarnings("serial") 
class SWEx2WorkerPanel extends JPanel { 
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
    private final JTextArea textArea = new JTextArea(20, 30); 
    private StartAction startAction = new StartAction("Start", KeyEvent.VK_S); 
    private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T); 
    private JProgressBar bar = new JProgressBar(); 
    private BackgroundTask backgroundTask; 
    private int processStatus; 

    public SWEx2WorkerPanel() { 
     setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     setLayout(new BorderLayout(5, 5)); 

     textArea.setFocusable(false); 
     JScrollPane sp = new JScrollPane(); 
     sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
     sp.setViewportView(textArea); 

     stopAction.setEnabled(false); 
     JPanel buttonPanel = new JPanel(); 
     buttonPanel.add(new JButton(startAction)); 
     buttonPanel.add(new JButton(stopAction)); 
     buttonPanel.add(bar); 

     add(statusLabel, BorderLayout.PAGE_START); 
     add(sp, BorderLayout.CENTER); 
     add(buttonPanel, BorderLayout.PAGE_END); 
    } 

    public void startProcess() { 
     if (backgroundTask != null && !backgroundTask.isDone()) { 
      return; // background task not yet done 
     } 
     textArea.setText(""); 
     startAction.setEnabled(false); 
     stopAction.setEnabled(true); 
     backgroundTask = new BackgroundTask(); 
     backgroundTask.addPropertyChangeListener(new BGTaskListener()); 
     backgroundTask.execute(); 
     bar.setIndeterminate(true); 
    } 

    public void cancelProcess() { 
     if (backgroundTask != null && !backgroundTask.isDone()) { 
      backgroundTask.cancel(true); 
     } 
    } 

    public void processStopped() { 
     statusLabel.setText((backgroundTask.getState()).toString() + " " 
       + processStatus); 
     stopAction.setEnabled(false); 
     startAction.setEnabled(true); 
     bar.setIndeterminate(false); 

     // Window thisWindow = SwingUtilities.getWindowAncestor(textArea); 
     // thisWindow.dispose(); 
    } 

    public int getProcessStatus() { 
     return processStatus; 
    } 

    private class BGTaskListener implements PropertyChangeListener { 
     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
       processStopped(); 
      } 

      // percolate this listener up to the main JPanel's Prop Chng 
      // Listener 
      SWEx2WorkerPanel.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), 
        evt.getNewValue()); 
     } 
    } 

    private class BackgroundTask extends SwingWorker<Integer, String> { 

     @Override 
     protected Integer doInBackground() throws Exception { 
      List<String> list = new ArrayList<>(); 
      list.add("ping"); 
      String name = System.getProperty("os.name"); 
      if (name.startsWith("Win")) { 
       list.add("-n"); 
      } else { 
       list.add("-c"); 
      } 
      list.add("5"); 
      list.add("google.com"); 
      try { 
       ProcessBuilder pb = new ProcessBuilder(list); 
       pb.redirectErrorStream(true); 
       Process p = pb.start(); 
       String s; 
       BufferedReader stdout = new BufferedReader(
         new InputStreamReader(p.getInputStream())); 
       while ((s = stdout.readLine()) != null && !isCancelled()) { 
        publish(s); 
       } 
       if (!isCancelled()) { 
        processStatus = p.waitFor(); 
       } 
       p.getInputStream().close(); 
       p.getOutputStream().close(); 
       p.getErrorStream().close(); 
       p.destroy(); 
      } catch (IOException | InterruptedException ex) { 
       ex.printStackTrace(System.err); 
      } 
      return processStatus; 
     } 

     @Override 
     protected void process(java.util.List<String> messages) { 
      statusLabel.setText((this.getState()).toString()); 
      for (String message : messages) { 
       textArea.append(message + "\n"); 
      } 
     } 

    } 

    private class StartAction extends AbstractAction { 
     public StartAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      startProcess(); 
     } 
    } 

    private class StopAction extends AbstractAction { 
     public StopAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      cancelProcess(); 
     } 
    } 

} 

編輯2

在您的最新代碼的迭代:

public static void main(String[] args) { 
    EventQueue.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Here I start"); 

      // note that the use of invokeLater twice is not necessary 
      SwingUtilities.invokeLater(() -> new SwingWorkerExample().displayGUI()); 

      // there is nothing in the code that pauses or delays this line of code 
      System.out.println("I'll show up when the SwingWorkerExample has finnished"); 
     } 
    }); 
} 

次要的問題:有沒有必要範圍內的已排隊的Swing線程上調用代碼從隊列Swing線程上的代碼。

關鍵問題:啓動GUI後,您的println語句立即生效 - 阻止它被調用的是什麼?你需要兩種機制中的一種來實現這種延遲。

  1. 或者使用回叫某種,任何形式的,或
  2. 顯示的圖像處理GUI作爲一個模式對話框,這樣一來就會耽誤從調用程序,它是在同一個線程的任何代碼並在對話框可見後調用。延遲的代碼將暫停,直到對話框不再存在。

第一種方式是兩者更靈活,因爲它不需要使用模態對話框。

上述機制的均爲的示例如下。注意您的代碼更改已標註爲\\ !!評論:

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.Dialog.ModalityType; 
import java.awt.event.*; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.ArrayList; 
import java.util.List; 

import javax.swing.*; 

import static javax.swing.Action.MNEMONIC_KEY; 

public class SwingWorkerExample2 { 
    private JDialog modalDialog; // !! a modal JDialog 
    private ExecuteFrame backgroundTask; 
    private Runnable myCallback; // !! 

    private class properyListener implements PropertyChangeListener { 
     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
       int processState = backgroundTask.getProcessStatus(); 
       System.out.println("Process State: " + processState + "\n"); 

       // !! added 
       myCallback.run(); // simply run it. No need to place into a new thread 
      } 
     } 
    } 

    // !! set myCallback field. This could also be done as a parameter to 
    // !! the displayGUI(Runnable myCallback) method if desired 
    public SwingWorkerExample2(Runnable myCallback) { 
     this.myCallback = myCallback; 
    } 

    private void displayGUI() { 
     // !! create it as a modal dialog 
     modalDialog = new JDialog(null, "Swing Worker Example", ModalityType.APPLICATION_MODAL); 
     modalDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); // !! 
     backgroundTask = new ExecuteFrame(); 
     modalDialog.add(backgroundTask); 
     backgroundTask.addPropertyChangeListener(new properyListener()); 
     modalDialog.pack(); 
     modalDialog.setLocationByPlatform(true); 
     modalDialog.setVisible(true); 
     backgroundTask.startProcess(); 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       System.out.println("Here I start"); 
       Runnable myCallback = new Runnable() { 
        @Override 
        public void run() { 
         System.out.println("This is being displayed from a Runnable"); 
        } 
       }; 

       // pass callback Runnable into SwingWorkerExample2's constructor 
       new SwingWorkerExample2(myCallback).displayGUI(); // !! not wrapped in a Runnable 
       System.out.println("This is displayed after the dialog is no longer visible"); 
      } 
     }); 
    } 
} 

@SuppressWarnings("serial") 
class ExecuteFrame2 extends JPanel { 
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
    private final JTextArea textArea = new JTextArea(20, 30); 
    private StartAction startAction = new StartAction("Start", KeyEvent.VK_S); 
    private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T); 
    private JProgressBar bar = new JProgressBar(); 
    private BackgroundTask backgroundTask; 
    private int processStatus; 

    public ExecuteFrame2() { 
     setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     setLayout(new BorderLayout(5, 5)); 
     textArea.setFocusable(false); 
     JScrollPane sp = new JScrollPane(); 
     sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
     sp.setViewportView(textArea); 
     stopAction.setEnabled(false); 
     JPanel buttonPanel = new JPanel(); 
     buttonPanel.add(new JButton(startAction)); 
     buttonPanel.add(new JButton(stopAction)); 
     buttonPanel.add(bar); 
     add(statusLabel, BorderLayout.PAGE_START); 
     add(sp, BorderLayout.CENTER); 
     add(buttonPanel, BorderLayout.PAGE_END); 
    } 

    public void startProcess() { 
     if (backgroundTask != null && !backgroundTask.isDone()) { 
      return; // background task not yet done 
     } 
     textArea.setText(""); 
     startAction.setEnabled(false); 
     stopAction.setEnabled(true); 
     backgroundTask = new BackgroundTask(); 
     backgroundTask.addPropertyChangeListener(new BGTaskListener()); 
     backgroundTask.execute(); 
     bar.setIndeterminate(true); 
    } 

    public void cancelProcess() { 
     if (backgroundTask != null && !backgroundTask.isDone()) { 
      backgroundTask.cancel(true); 
     } 
    } 

    public void processStopped() { 
     statusLabel.setText((backgroundTask.getState()).toString() + " " + processStatus); 
     stopAction.setEnabled(false); 
     startAction.setEnabled(true); 
     bar.setIndeterminate(false); 
     // Window thisWindow = SwingUtilities.getWindowAncestor(textArea); 
     // thisWindow.dispose(); 
    } 

    public int getProcessStatus() { 
     return processStatus; 
    } 

    public class BGTaskListener implements PropertyChangeListener { 
     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
       processStopped(); 
      } 
      // percolate this listener up to the main JPanel's Prop Chng 
      // Listener 
      ExecuteFrame2.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), 
        evt.getNewValue()); 
     } 
    } 

    private class BackgroundTask extends SwingWorker<Integer, String> { 
     @Override 
     protected Integer doInBackground() throws Exception { 
      List<String> list = new ArrayList<>(); 
      list.add("ping"); 
      String name = System.getProperty("os.name"); 
      if (name.startsWith("Win")) { 
       list.add("-n"); 
      } else { 
       list.add("-c"); 
      } 
      list.add("5"); 
      list.add("google.com"); 
      try { 
       ProcessBuilder pb = new ProcessBuilder(list); 
       pb.redirectErrorStream(true); 
       Process p = pb.start(); 
       String s; 
       BufferedReader stdout = new BufferedReader(
         new InputStreamReader(p.getInputStream())); 
       while ((s = stdout.readLine()) != null && !isCancelled()) { 
        publish(s); 
       } 
       if (!isCancelled()) { 
        processStatus = p.waitFor(); 
       } 
       p.getInputStream().close(); 
       p.getOutputStream().close(); 
       p.getErrorStream().close(); 
       p.destroy(); 
      } catch (IOException | InterruptedException ex) { 
       ex.printStackTrace(System.err); 
      } 
      return processStatus; 
     } 

     @Override 
     protected void process(java.util.List<String> messages) { 
      statusLabel.setText((this.getState()).toString()); 
      for (String message : messages) { 
       textArea.append(message + "\n"); 
      } 
     } 
    } 

    private class StartAction extends AbstractAction { 
     public StartAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      startProcess(); 
     } 
    } 

    private class StopAction extends AbstractAction { 
     public StopAction(String name, int mnemonic) { 
      super(name); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      cancelProcess(); 
     } 
    } 
} 
+0

你好@Hovercraft,謝謝你的回答。顯示輸出已經由鏈接中提供的代碼完成。我不明白你如何告訴代碼在其他地方等待。我的意思是,你調用myworker.execute(),然後繼續使用那個方法。你如何等待(SwingWorker.StateValue.DONE == evt.getNewValue())下的代碼被執行? – nck

+1

@nck:你不告訴代碼「等待」,而是這是事件驅動的編程 - 你響應**事件**,這裏事件是SwingWorker正在完成。如果您仍不確定要做什麼,請考慮通過發佈有效且體面的[mcve]來澄清您的問題和代碼,這是一個小型可編譯和可運行的演示程序,可以幫助我們瞭解您的問題。也許在這個MCVE中,使用Thread.sleep來模擬你的後臺進程。 –

+0

我剛剛添加了代碼副本,顯示我的意思,我不希望在ping完成之前打印第二個字符串。 – nck

-1

可以使用代替線程。試試這個:

Thread t = new Thread(new Runnable(){ 
    @Override 
    public void run() 
    { 
    //The stuff you want to finish first 
    } 
}).start(); 
t.join(); 

這將創建一個新的線程並等待它完成。

+0

對不起,但這並不能解決他的問題。通過加入線程,您完全消除了線程在後臺進程中的運行,因此此代碼將凍結Swing GUI。更好地使用回調機制,監聽器,幸運的是,這種機制已經嵌入到SwingWorker中。 –