直到Runtime.getRuntime()。exec()完成执行后才会显示Swing消息

时间:2013-03-13 21:19:49

标签: java swing process

我是Swing的新手。我正在尝试创建一个swing包装器,允许用户浏览并选择一个文件夹,该文件夹路径用作控制台.exe程序的命令行参数。在他们选择文件夹并单击“启动程序”按钮后,我希望摆动窗口显示一条消息,告诉他们程序正在处理(并显示一个时钟的GIF动画),运行外部程序,然后显示另一条消息当该程序完成执行时。我遇到的问题是“处理”消息直到外部程序完成执行后才会显示。在下面的代码中,单击“启动程序”按钮时会执行onLaunchProgram方法。我尝试过revalidate()和repaint(),但是没有变化。我有一个“完成”消息的waitFor(),但即使我把它拿出来,“处理”消息和gif也不会显示,直到外部程序完成执行。


    ...

    JTextField txtFolder = new JTextField();
    JLabel lblMessage = new JLabel();
    JLabel lblPic = new JLabel();
    JButton btnLaunchApplication = new JButton("Launch Program");  

    ...  

    btnLaunchApplication.addActionListener(new ActionListener() {  
        public void actionPerformed(ActionEvent evt) {  
            onLaunchProgram(evt);  
        }  
     });  

    ...

    if (returnVal == JFileChooser.APPROVE_OPTION){
        file = fc.getSelectedFile();
        txtFolder.setText(file.getAbsolutePath());
    }

    ...

    private void onLaunchProgram(ActionEvent evt) {
        String strExecutableFilename = "MyExecutableProgam";
        String strSourceFolder = txtFolder.getText();
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        lblMessage.setText("Processing");
        ImageIcon icon = new ImageIcon("clock.gif");
        lblPic.setIcon(icon);
        try {
            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }
            lblMessage.setText("Finished");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

2 个答案:

答案 0 :(得分:3)

您的示例代码很难确定如何执行onLaunchProgram方法,但是从您的描述中,假设您在事件调度线程的上下文中执行它将是一种安全的方法。 / p>

Event Dispatching Thread负责(除其他事项外)调度重绘请求。阻止此线程的任何事情都会阻止它更新UI。

因为procCommand.waitFor()是阻止操作,所以这将阻止任何重绘请求(或任何事件)在返回之前被处理。

您应该在单独的线程中执行所有耗时或阻塞进程。但是你遇到的问题是,所有对UI的更新都要在EDT的上下文中执行(也就是说,你永远不应该从EDT以外的任何线程更改/更新/修改/创建任何UI组件)

在Swing中你有很多选择,在你的情况下,我建议使用SwingWorker。它允许您在后台线程中执行该过程,但有一些易于使用的方法来重新同步UI的更新。

public class ProcessWorker extends SwingWorker<Integer, String> {

    private String program;
    private String sourceFolder;

    public ProcessWorker(String program, String sourceFolder) {
        this.program = program;
        this.sourceFolder = sourceFolder;
    }

    @Override
    protected void process(List<String> chunks) {
        // Back on the EDT
        for (String value : chunks) {
            if (value.equalsIgnoreCase("PROCESSING")) {
                lblMessage.setText("Processing");
                ImageIcon icon = new ImageIcon("clock.gif");
                lblPic.setIcon(icon);
            } else if (value.equalsIgnoreCase("FINISHED")) {
                lblMessage.setText("Finished");
            } else {
                // Possible some other message...
            }
        }
    }

    @Override
    protected Integer doInBackground() throws Exception {
        int result = -1;

        String strExecutableFilename = program;
        String strSourceFolder = sourceFolder;
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        publish("PROCESSING");
//        lblMessage.setText("Processing");
//        ImageIcon icon = new ImageIcon("clock.gif");
//        lblPic.setIcon(icon);
        try {
            ProcessBuilder pb = new ProcessBuilder(program);
            pb.redirectError();
            pb.directory(new File(strSourceFolder));
            Process procCommand = pb.start();
//            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                result = procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }
//            lblMessage.setText("Finished");
            publish("FINISHED");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
}

您还应该熟悉ProcessBuilder。它有许多有用的方法来构建过程,并克服了人们在尝试Runtime.getRuntime().exec工作时遇到的一些困难。

您应该查看http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html了解更多详情

答案 1 :(得分:0)

看来你在一个线程中都在做这一切。

使用事件派发线程来调用你的gui代码。

private void onLaunchProgram(ActionEvent evt) {
        String strExecutableFilename = "MyExecutableProgam";
        String strSourceFolder = txtFolder.getText();
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        ImageIcon icon = new ImageIcon("clock.gif");
        javax.swing.SwingUtilities.invokeLater(
            new Runnable() {
                 public void run() {
                     lblMessage.setText("Processing");
                     lblPic.setIcon(icon);
                 }
            });

        try {
            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }

            javax.swing.SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                      lblMessage.setText("Finished");
                    }
                 });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }