1

我正在尝试运行异步进程并获取其输入流(如果有)。

这是我的代码:

    CommandCall commandCall = new CommandCall(commands);
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<Integer> task = executor.submit(commandCall);

这是进程运行任务

public class CommandCall implements Callable<Integer> {

    private byte[] output;
    private int retval=-1;
    private String[] commands=null;
    Process process=null;

    public CommandCall(String[] commands) throws Exception {
        this.commands=commands;
        this.output=null;
    }

    public void destroy() {
        try {
            if(process!=null) process.destroy();
        }catch(Exception e){}
    }

    public byte[] getByteArray() {
        return output;
    }


    public int getRetval() {
        return retval;
    }

    @Override
    public Integer call() throws Exception {
        try{
            process = new ProcessBuilder(commands).start();
            // here i must read the process input and store it to this.output
            // this must be a non lockable read because the process can be without any output
            retval= process.waitFor();
        }finally{
            try{
                if(bos!=null) bos.close();
            }catch(Exception e){}
        }
        return retval;
    }

}

我无法获得流程输出,请注意两件非常重要的事情:

  • 进程必须是异步的,因为我需要管理超时
  • Process 的 InputStream 可以是可选的,并且不能锁定等待内容的线程:可以有一个没有任何输出的进程。

更新

我正在尝试这个版本......似乎工作,但我不知道它是否足够强大。

@Override
public Integer call() throws Exception {
    InputStream is=null;
    try{
        process = new ProcessBuilder(commands).start();
        is=process.getInputStream();
        int len;
        int size = 1024;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] buf = new byte[size];
        while ((len = is.read(buf, 0, size)) != -1)
            bos.write(buf, 0, len);
        output = bos.toByteArray();
        retval= process.waitFor();
    }finally{
        try{
            if(is!=null) is.close();
        }catch(Exception e){}
    }
    return retval;
}
4

1 回答 1

0

观察进程输出的行为本身应该在自己的线程中。一旦进程终止,该线程也应该终止。

所以基本上你可以这样做:

@Override
public Integer call() throws Exception {
    Thread outputObserver = null;

    try(ByteArrayOutputStream  baos = new ByteArrayOutputStream()) {
        this.process = new ProcessBuilder(commands).start();
        outputObserver = new OutputObserver(process.getInputStream(), baos);
        outputObserver.start();
        this.retval = process.waitFor();
        this.output = baos.toByteArray();
    }finally{
        if(outputObserver != null) outputObserver.interrupt();
    }

    return this.retval;
}

private static OutputObserver extends Thread {
    private final InputStream input;
    private final OutputStream output;

    OutputObserver(InputStream input, OutputStream output) {
        this.input = input;
        this.output = output;
    }

    @Override
    public void run() {
         while(!Thread.interrupted()) {
             // Copy bytes from input to output here (handling any exceptions).
             // Maybe use 3rd party libs for that.
         }
         // Make sure to copy remaining bytes here, too.
    }
}

几点注意事项:

  • 我没有测试任何代码。也许我犯了一些微妙的错误,但方向应该是明确的。
  • 通常我不会扩展Thread而是实施Runnable. 我只是不想让事情变得过于复杂。
  • 我没有提供将字节从输入流复制到输出流的代码。如果您需要这方面的帮助,请在网络上进行一些搜索。你会找到很多解决方案。
于 2014-10-17T12:18:17.990 回答