65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Java 中更轻松地执行外部进程

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (16投票s)

2004年4月8日

CPOL

3分钟阅读

viewsIcon

63777

downloadIcon

1908

无忧地从 Java 代码执行任何外部进程

Sample Image - ProcessHelper.jpg

引言

有时,当您依赖于从 Java 代码执行外部进程时,实际让该代码正确运行可能非常痛苦。

尝试运行外部进程时存在几个陷阱

  1. 来自进程和到进程的输入和输出流未得到正确处理。
  2. 即使进程正在运行,您的应用程序也没有等待它完成。
  3. 您的应用程序通过简单地停止来等待进程完成。
  4. 进程已终止,但您没有收到结果代码。
  5. 您根本不知道如何实际运行外部进程!

如果这些问题曾经困扰过您,请继续阅读。

我将使用 DOS 提示符(在 WinXP 下)作为本文演示的外部进程。

源代码组成如下

  • 可重用代码
    • dev.exec.util.ExecHelper
    • dev.exec.util.ExecProcessor
  • 演示代码
    • dev.exec.tester.TestApplication
    • dev.exec.tester.TestFrame

想法很简单

  1. TestApplication 创建一个 TestFrame 的实例。
  2. TestFrame 显示给用户。
  3. 用户请求执行命令处理器。
  4. TestFrame 将自己注册到 ExecHelper 作为 ExecProcessor
  5. TestFrame 使用 ExecHelper 运行命令处理器。
  6. ExecHelper 通知 TestFrame 关于新的输出文本。
  7. 用户使用 TestFrame 为命令处理器输入命令。
  8. TestFrames 通知 ExecHelper 关于新的用户输入。
  9. ExecHelper 通知命令处理器关于新的用户输入。
  10. 当命令处理器正在运行时,循环执行 6 - 10。
  11. 当程序正在运行时,循环执行 3 - 11。
  12. 退出。

源文件已记录(至少 ExecHelperExecProcessor),并且演示 ZIP 包含一个 JBuilder 项目以及编译后的类和一个 JAR 文件。

我现在将解释如何在可重用代码中实现该概念。

首先,我声明 ExecProcessor 作为一个接口,用于处理来自外部进程的所有事件。

public interface ExecProcessor {
  // This method gets called when the process sends us a new input String..
  public void processNewInput(String input);
  // This method gets called when the process sends us a new error String..
  public void processNewError(String error);
  // This method gets called when the process has ended..
  public void processEnded(int exitValue);
}

实现 ExecProcessor 接口的 TestFrame 具有以下方法

public void processNewInput(String input) {
  updateTextArea(jTextArea1, input);
}

public void processNewError(String error) {
  updateTextArea(jTextArea1, error);
}

public processEnded(int exitValue) {
  exh = null;
  statusBar.setText("Command.exe ended..");
  JOptionPane.showMessageDialog(this, "Exit value for Command.exe was [" 
    + exitValue + "]", "Command.exe is done!", JOptionPane.INFORMATION_MESSAGE);
  try {
    Thread.sleep(1000);
  } catch (InterruptedException ex) {
  }
  jTextArea1.setText(null);
  statusBar.setText("Ready..");
}

如你所见,事情很简单。 processNewInput()processNewError() 方法使用从命令处理器发送的新文本行更新一个 TextArea,而 processEnded() 方法通知程序命令处理器已终止,并显示从命令处理器发送的退出值。

TestFrame 本身使用 runCommandActionPerformed() 方法调用 ExecHelperexec() 方法,从而执行命令处理器,如下面的代码所示

void runCommandActionPerformed(ActionEvent e) {
  if (exh == null) {
    try {
      exh = ExecHelper.exec(this, "cmd.exe");
      statusBar.setText("Command.exe running..");
    } catch (IOException ex) {
      processNewError(ex.getMessage());
    }
}

剩下的就是描述 ExecHelper 如何执行它的魔术。 首先,ExecHelper 是一个 Runnable 对象,这意味着它有一个 run() 方法,允许它在应用程序本身继续运行时执行其任务(即,多线程)。

ExecHelper 有三个流要处理,以及三个线程要处理。 是的,此版本的代码要求为每个进程激活三个单独的线程。 可能有一种方法可以用更少的线程来完成此操作,但目前我还没有时间进行研究。

作为一个 Runnable 对象,ExecHelperrun() 方法是最有趣的

public void run() {
  // Are we on the process Thread?
  if (processThread == Thread.currentThread()) {
    try {
      // This Thread just waits for the process to end and notifies the handler..
      processEnded(process.waitFor());
    } catch (InterruptedException ex) {
      ex.printStackTrace();
    }
  // Are we on the InputRead Thread?
  } else if (inReadThread == Thread.currentThread()) {
    try {
      // Read the InputStream in a loop until we find no more bytes to read..
      for (int i = 0; i > -1; i = pInputStream.read(inBuffer)) {
        // We have a new segment of input, so process it as a String..
        processNewInput(new String(inBuffer, 0, i));
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  // Are we on the ErrorRead Thread?
  } else if (errReadThread == Thread.currentThread()) {
    try {
      // Read the ErrorStream in a loop until we find no more bytes to read..
      for (int i = 0; i > -1; i = pErrorStream.read(errBuffer)) {
        // We have a new segment of error, so process it as a String..
        processNewError(new String(errBuffer, 0, i));
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

我建议你自己查看源代码,了解用户输入如何从 TestFrame 流向命令处理器。

我希望这对需要在 Java 中使用此功能的任何人都有用。

历史

  • 2004 年 4 月 7 日:首次发布
© . All rights reserved.