1

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

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

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

4

2 回答 2

3

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

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

另外,我不是 100% 熟悉 Android,但在普通的 java 中,最好使用ProcessBuilder生成子Process实例。这是因为ProcessBuilder可以让您将孩子的 stderr 和 stdout 组合在同一个流中,这样您就可以通过从process.getInputStream().

于 2012-12-10T19:15:36.447 回答
0

好的,我查看了 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;
于 2012-12-11T08:32:05.410 回答