过程输出仅在过程完成后才可用

我有一个Runnable从外部调用的exe读取Console输出(见下文)并将其写入日志文件和JTextArea。

但是我的Runnable在exe完全完成之前不会在JTextArea中显示控制台输出。 如何实现打印控制台输出?

简短简洁代码示例如下:

//主要

import java.awt.*; import java.io.IOException; import javax.swing.*; public class Example extends JFrame { private static final long serialVersionUID = 1L; public static int maxX, maxY; public static JTextArea ta = new JTextArea(20, 60);//For LOG display window public static void main(String args[] ) throws IOException { new Example(); } public Example() { this.setTitle("Example"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //MAIN Panel final JPanel main = new JPanel(); JButton RunButton = button.run(main); main.add(RunButton); Container container = getContentPane(); container.add(main); this.pack(); this.setVisible(true); } } 

//按钮动作侦听器

 import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.*; public class button { public static JButton run( final JPanel parent ) { JButton RunButton = new JButton(); RunButton.setText("Start!"); RunButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent event) { try { //Set up LOG Display JDialog dialog = new JDialog((JFrame)null, "Working..."); JPanel temp_panel = new JPanel(); temp_panel.add(new JScrollPane(Example.ta)); dialog.getContentPane().add(temp_panel); dialog.pack(); dialog.setVisible(true); //Build the Command ArrayList command = new ArrayList(); command.add("ping"); command.add("127.0.0.1"); //Start the process Process p = new ProcessBuilder(command).start(); //Starts LOG display capture in separate thread SwingUtilities.invokeLater(new execute(p)); //Wait for call to complete p.waitFor(); } catch(Exception err) { JOptionPane.showMessageDialog( parent, "Error Executing Run!", "Warning", JOptionPane.ERROR_MESSAGE ); } }//end ActionPerformed }); return RunButton; } } 

// Runnable接口

 import java.io.BufferedReader; import java.io.InputStreamReader; public class execute implements Runnable { String line; Process p; public execute ( Process process ) { p = process; } public void run() { try { //Read Process Stream Output and write to LOG file BufferedReader is = new BufferedReader( new InputStreamReader(p.getInputStream())); while ( (line = is.readLine()) != null ) { Example.ta.append(line + "\n"); } System.out.flush(); } catch(Exception ex) { ex.printStackTrace(); } } } 

也许是因为你不尊重Swing的线程策略 。 所有对swing组件的访问都必须在事件派发线程中完成。 因此,您的runnable应该使用SwingUtilities.invokeLater来更新EDT中的文本区域,而不是在单独的线程中。

编辑:正如他在评论中提到的那样: JTextArea.append是线程安全的,所以这里并不是绝对需要的。 不过,我仍然会这样做,因为如果文本区域的附加被任何其他Swing交互替换或补充,那么它将不再是线程安全的。

也可能是外部进程没有发送任何换行符,这使得readLine阻塞直到找到一个或者到达通信结束。

只是为了帮助矿工 – 下面是一个完整的简约(遗漏了一切并非绝对必要)的例子,确实在我的上下文中有效:每行在textArea中显示为read。 它基本上是使用Justin建议的SwingWorker,为了清晰起见,重新安排了一些东西。

 public class ProcessExample { public static class ProcessWorker extends SwingWorker { private JTextArea ta; private List process; public ProcessWorker(List command, JTextArea ta) { this.process = command; this.ta = ta; } @Override protected Void doInBackground() throws Exception { Process p = new ProcessBuilder(process).start(); // Read Process Stream Output and write to LOG file BufferedReader is = new BufferedReader(new InputStreamReader( p.getInputStream())); String line; while ((line = is.readLine()) != null) { publish(line); } is.close(); return null; } @Override protected void process(List chunks) { for (String string : chunks) { ta.append(string + "\n"); } } } private void startProcess(JTextArea ta) { ArrayList command = new ArrayList(); command.add("ping"); command.add("127.0.0.1"); new ProcessWorker(command, ta).execute(); } private JComponent getContent() { JPanel main = new JPanel(new BorderLayout()); final JTextArea ta = new JTextArea(20, 60); main.add(new JScrollPane(ta)); Action action = new AbstractAction("Start!") { @Override public void actionPerformed(ActionEvent e) { startProcess(ta); } }; main.add(new JButton(action), BorderLayout.SOUTH); return main; } public static void main(String args[]) throws IOException { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new JFrame("Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new ProcessExample().getContent()); frame.pack(); frame.setVisible(true); } }); } } 

您可以使用SwingWorker尝试相同的逻辑。 您可以扩展此类,而不是实现runnable。 它可以将你的Text区域作为参数,你可以发布数据,而不必处理SwingUtils.invokeLater,这很容易…

尝试:

 public class execute extends javax.swing.SwingWorker { String line; Process p; JTextArea jta; File f = new File( properties.prop.getProperty( "LOG_FILE_DIR" ) + "\\PartGen.log"); public execute ( Process process , JTextArea jta ) { p = process; this.jta = jta; } //implements a method in the swingworker public void doInBackground() throws Exception { //Read Process Stream Output and write to LOG file BufferedReader is = new BufferedReader( new InputStreamReader(p.getInputStream())); while ( (line = is.readLine()) != null ) { osfile.writeline(line, f); publish(new String(line + "\n")); } System.out.flush(); return null; } //This will happen on the UI Thread. public void process(List lines){ for(Object o : lines){ jta.append((String)o); } } public void done(){ try{ get(); //You will get here if everything was OK. So show a popup or something to signal done. }catch(Exception ex){ //this is where your IO Exception will surface, should you have one. } } } 

另外,在你的调用代码中,我假设它在你的ui某处:

  Process p = new ProcessBuilder(command).start(); execute ex = new execute( p , yourTextArea); ex.execute(); 

我没有尝试编译这个,所以你可能不得不检查API,但希望它会给你一个如何做的要点。

问题不在于线程没有捕获数据,而是JTextArea只是没有刷新。 repaint()revalidate()updateUI()没有刷新JTextArea,但是以下做了:

 Example.ta.update(Example.ta.getGraphics()); 

这种情况下的问题是waitFor:

 p.waitFor(); 

这会导致Button Action Listener在该点上等待,直到该过程完成。