如何在stdout / stderr准备就绪时进行回调,而不是忙于轮询?

时间:2018-11-02 06:54:05

标签: java android process io

我发现从Process标准输出中读取的所有讨论都以阻塞方式进行,例如:

Process process=Runtime.getRuntime().exec(cmd);
BufferedReader in=new BufferedReader(
    new InputStreamReader(process.getInputStream()));
for(String line;(line=in.readLine())!=null;)
{
    useText(line);
}

如果我尝试避免阻塞,我的选择是使用in.ready()轮询流。

但是我真正需要的是让进程继续运行并执行一些有用的操作,并且仅在数据实际可用时才尝试读取任何内容。因此,我正在寻找类似Qt的QProcess::readyReadStandardOutput信号。请注意,BufferedReader的网络ready不是我要寻找的:它只是我必须反复检查的一种方法,而不是任何种类的信号。

Java中有类似的东西吗?

1 个答案:

答案 0 :(得分:2)

如果有事件循环,则可以启动后台线程以将事件添加到该事件循环。

例如

ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true); // capture errors as well.
Process process = pb.start();
BufferedReader in = new BufferedReader(
        new InputStreamReader(process.getInputStream()));
Thread t = new Thread(() -> {
    try {
        for (String line; (line = in.readLine()) != null; ) {
            Activity.runOnUiThread(() -> useText(line));
        }
    } catch (Exception e) {
        logger.log(e);
    } finally {
        Activity.runOnUiThread(() -> noMoreText());
    }
});
t.setDaemon(true);
t.start();

您可以有一个仅执行queue.add(in.readLine())的线程,然后您的主线程可以轮询此队列。这样,所有的多线程问题就变得简单且包含在内。

static final String EOF = new String(); // use for == comparison later

ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true); // capture errors as well.
Process process = pb.start();
BufferedReader in = new BufferedReader(
        new InputStreamReader(process.getInputStream()));
int capacity = 1024;
BlockingQueue<String> lines = new ArrayBlockingQueue<>(capacity);
Thread t = new Thread(() -> {
    try {
        for (String line; (line = in.readLine()) != null; ) {
            lines.add(line);
            while (lines.remainingCapacity() < 2) // don't run out of memory if too much.
                Thread.sleep(50);
        }
    } catch (Exception e) {
        logger.log(e);
    } finally {
        lines.offer(EOF);
    }
});
t.setDaemon(true);
t.start();

// later in a loop
String line = lines.poll();
if (line == null) // no data yet.

else if (line == EOF) // we have the EOF marker.