【问题标题】:Runtime.getRuntime execRuntime.getRuntime 执行
【发布时间】:2012-12-10 18:10:46
【问题描述】:

我创建了一个类来处理 android 应用程序中的根命令,它大部分工作得很好。我使用 Runtime.getRuntime().exec("su") 创建进程,然后使用 DataOutputStream 输入命令,并使用 DataInputStream 和已弃用的 readLine 从命令中获取数据。 (我已经尝试使用 BufferedReader 代替,但问题没有区别)。

我的问题是,如果命令产生错误,应用程序将挂起。 F.eks。如果我执行命令 "[ -f /test ] && md5sum /test" || echo 0" 我不会有任何问题。但是,如果我执行 "md5sum /test" 并且文件不存在,我将不得不强制关闭应用程序,因为它将无处可去。在这个例子中,解决方案是有原因的像在第一个示例中那样检查文件,但并非所有情况都如此简单。可能会发生问题,当它们发生时,人们不应该强制关闭应用程序。

有没有办法解决这个问题?

【问题讨论】:

  • 您可能没有完全正确地读取它的 stdout/stderr 流
  • 好的,你能举个例子说明如何正确地做到这一点吗?

标签: java android


【解决方案1】:

您的应用可能挂起,因为您可能没有处理由衍生进程生成的 stderr/stdout。这背后的原因是衍生进程的输出缓冲区非常小(通常只有几千字节)。一旦这些缓冲区已满,该进程将挂起,直到其缓冲区释放足够的空间以继续写入输出文本。我怀疑您在运行第二个命令时遇到问题,因为第二个命令失败并生成大量控制台输出。您的子进程用完缓冲区空间,然后尝试阻塞,直到有更多空间可用,这永远不会发生。

Runtime.getRuntime().exec() 返回Process 的实例。 Process 对象有一个访问器方法(我相信它是getInputStream()),允许您使用它的标准输出。您还需要对getErrorStream() 执行相同的操作。通常,我得到InputStream,然后有一个单独的线程不断使用来自InputStream 的数据,直到它关闭。您不需要对数据本身做任何事情,只需阅读它即可。这将告诉底层进程它可以清除其输出缓冲区,希望在它们变满之前(因此,导致进程阻塞)。

另外,我不是 100% 熟悉 Android,但在普通的 Java 中,最好使用 ProcessBuilder 来生成子 Process 实例。这是因为ProcessBuilder 允许您将孩子的 stderr 和 stdout 组合在同一个流中,这样您就可以通过从process.getInputStream() 返回的流中读取数据来在单个线程中使用这两者。

【讨论】:

  • 哇,谢谢。这很有意义。我会玩这个,也看看 ProcessBuilder。再次感谢:)
【解决方案2】:

好的,我已经查看了 ProcessBuilder(也适用于 android)。但是我还是遇到了问题。

while ((line = buffer.readLine()) != null) {
    data = line;
}

if (data != null && data.contains("uid=0")) {
    return true;
}

这将提供与以前相同的问题。它无处可去。但是,如果我将代码更改为

while ((line = buffer.readLine()) != null) {
    if (data != null && data.contains("uid=0")) {
        return true;
    }
}

这会很好。该过程将结束,它将返回true。问题是,为了让我使用它,我必须确切地知道返回数据的期望值(我在这个测试方法中这样做)。所以这不是解决办法。

这是整个测试方法

try {
    ProcessBuilder builder = new ProcessBuilder("su");
    builder.redirectErrorStream(true);

    Process process = builder.start();
    DataOutputStream output = new DataOutputStream(process.getOutputStream());
    InputStreamReader reader = new InputStreamReader(process.getInputStream());
    BufferedReader buffer = new BufferedReader(reader);

    output.writeBytes("id\n");
    output.flush();

    String line = null;
    String data = null;
    while ((line = buffer.readLine()) != null) {
        data = line;
    }

    if (data != null && data.contains("uid=0")) {
        return true;
    }

} catch(Throwable e) {
    Log.d(TAG, "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
}

return false;

【讨论】:

  • 好的,我提出了一个可以正常工作的解决方案。在 output.writeBytes(cmd + "\n") 之后,在 output.flush() 之前添加 output.writeBytes("echo EOL:a00c38d8:EOL\n")。然后在循环中你可以做 while ((line = buffer.readLine()) != null && !"EOL:a00c38d8:EOL".equals(line)) 并抓住最后一行,在 readLine 挂起之前,等待下一个线进来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多