从Java程序执行ADB命令

8

我正在开发的程序使用ADB(Android Debug Bridge)将文件发送到我的手机:

for (String s : files)
    String cmd = "adb -s 0123456789ABCDEF push " + s + " /mnt/sdcard/" + s;
    try {
        InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
        while (is.read() != -1) {}
    } catch (IOException e) {
        e.printStackTrace();
    }

我希望程序等待ADB传输完成,但ADB作为一个守护进程运行,因此永远不会完成。但程序立即继续执行,某种方式文件没有发送到我的手机(日志中没有异常)。当我从控制台运行命令时,它可以正常工作。
我做错了什么?如何正确地通过ADB发送文件?
注意:`is.read() == -1` 不起作用,因为ADB守护进程将所有输出写入系统标准输出。我尝试将其转发到文本文件中。它保持为空,输出仍然写入终端。
编辑:读取ADB进程的ErrorStream返回每个 `adb push` 命令的adb帮助。再次强调:确切的命令(从Eclipse控制台复制)在终端中可以工作。
编辑2:使用ProcessBuilder而不是 `RUntime.getRuntime.exec()` 导致以下错误:
java.io.IOException: Cannot run program "adb -s 0123456789ABCDEF push "inputfile "outputfile""": error=2, File or directory not found

在ProcessBuilder的start()方法中,使用绝对路径启动ADB(/usr/bin/adb)时也会发生相同的情况。输入文件和输出文件字符串也是绝对路径,例如/home/sebastian/testfile,并且肯定存在。当从终端运行命令(打印字符串“cmd”,复制粘贴)时,一切仍然正常工作。

你最近的观察是目前最好的线索。只是为了确认,您的文件名中没有特殊字符。并且您确保在Shell和程序中都选择相同的adb可执行文件。ADB错误输出是否仅包含常规帮助或某些不喜欢的特定内容? - Harald
两者都只使用“adb”,它指的是/usr/bin/adb。唯一的特殊字符是下划线,不应该引起任何问题。并且在错误日志中输出与adb help完全相同。进程的输出(getInputStream())为空。 - s3lph
在我的程序输出的ADB帮助末尾有一个\uFFFF字符,这正常吗?控制台中转发的adb help命令没有这个问题。 - s3lph
5个回答

11
我用以下方法解决了这个问题:
public class Utils {
    private static final String[] WIN_RUNTIME = { "cmd.exe", "/C" };
    private static final String[] OS_LINUX_RUNTIME = { "/bin/bash", "-l", "-c" };

    private Utils() {
    }

    private static <T> T[] concat(T[] first, T[] second) {
        T[] result = Arrays.copyOf(first, first.length + second.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

    public static List<String> runProcess(boolean isWin, String... command) {
        System.out.print("command to run: ");
        for (String s : command) {
            System.out.print(s);
        }
        System.out.print("\n");
        String[] allCommand = null;
        try {
            if (isWin) {
                allCommand = concat(WIN_RUNTIME, command);
            } else {
                allCommand = concat(OS_LINUX_RUNTIME, command);
            }
            ProcessBuilder pb = new ProcessBuilder(allCommand);
            pb.redirectErrorStream(true);
            Process p = pb.start();
            p.waitFor();
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String _temp = null;
            List<String> line = new ArrayList<String>();
            while ((_temp = in.readLine()) != null) {
                System.out.println("temp line: " + _temp);
                line.add(_temp);
            }
            System.out.println("result after command: " + line);
            return line;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

如果您的.bash_profile中不需要环境变量,请删掉“-l”参数。
我有一台Mac,但这个方法也适用于Linux。

检查新编辑的答案,它可以在旧版Win操作系统上运行,但尚未在Win8上测试。 - Sarpe
1
感谢您的努力。然而,在Win8上(也许在旧的Win操作系统上也是如此?),编译器会抱怨语句Process proc = new ProcessBuilder(ADBExecutor.WIN_RUNTIME, "adb devices").start();The constructor ProcessBuilder(String[], String) is undefined。因此,我将其编辑为Process proc = new ProcessBuilder("cmd.exe", "/C", this.adb_directory, "devices").start();,这样就可以工作了。请检查一下。 - hengxin
是的,你说得对!我已经进行了编辑,并放置了与我的代码库中使用的相同实现。 - Sarpe

11

我终于让它正常工作了:

ProcessBuilder pb = new ProcessBuilder("adb", "-s", "0123456789ABCDEF", "push", inputfile, outputfile);
Process pc = pb.start();
pc.waitFor();
System.out.println("Done");

我不知道ProcessBuilder在字符串中有什么问题,但最终它可以正常工作了...


肯定的是 ProcessBuilder 不能正确处理带有空格的字符串,我现在传递了一个 ArrayList,它正常工作了,感谢您的答案。 - moxi

0
 public static void adbpush() {
        System.out.println("adb push....");
        String[] aCommand = new String[] { adbPath, "push", inputFile(String),OutputDirectory };
        try {
            // Process process = new ProcessBuilder(aCommand).start();
            Process process = Runtime.getRuntime().exec(aCommand);
            process.waitFor(3, TimeUnit.SECONDS);
            System.out.println("file pushed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

0

建议使用完整路径执行ADB:像这样 $ANDROID_HOME/platform-tools/adb devices

这是您可以使用的完整代码:

String cmd = "$ANDROID_HOME/platform-tools/adb devices";
ProcessBuilder processBuilder = new ProcessBuilder();
if (Config.osName.contains("Windows"))
    processBuilder.command("cmd.exe", "/c", cmd);
else
    processBuilder.command("bash", "-c", cmd);

Process process = processBuilder.start();

0
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
string cmd = "/system/bin/input keyevent 23\n";
os.writeBytes(cmd);

手机必须已经取得root权限。在这里我已经执行了adb命令“input keyevent 23”。请记住,当您通过su执行adb命令时,不需要添加“adb shell input keyevent 23”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接