0

尝试android start-server从 Java 内部作为外部进程运行时,我遇到了各种问题。Java 由 Gradle 调用。让我向您描述一下在各种情况下到底发生了什么:

环境

  • 视窗 7 X64
  • 爪哇 7
  • commons-exec-1.1
  • 摇篮 1.6
  • 安卓 API 17
  • IntelliJ IDEA 12.1.4 社区版

假设
adb 守护进程被杀死并在调用adb start-server.

情况1

这段代码:

DefaultExecutor executor = new org.apache.commons.exec.DefaultExecutor();
executor.execute(org.apache.commons.exec.CommandLine.parse("adb start-server"));
log.info("Checkpoint!");

从应用程序插件的 Gradlerun任务运行时,将显示启动服务器输出,即:

* daemon not running. starting it now on port 5037 *
* daemon started successfully *

然后它将挂起,即“检查点!” 永远不会被记录。手动终止adb.exe进程将导致代码继续执行。

问题 1

为什么这个调用会阻塞?当adb start-server从终端运行命令时,几秒钟后控件返回到终端,为什么它不会在代码中发生?

案例2

相反,如果我像这样直接使用 Java 运行时:

Runtime.getRuntime().exec(new String[]{"adb", "start-server"});
log.info("Checkpoint!");
System.exit(0);

如果像以前一样从 Gradle 调用,“检查点!” 将被记录。但是,执行将挂起System.exit(0)。手动杀死adb.exe将再次使 Gradle 调用完成。

在这种情况下,不adb start-server显示输出。

有趣的是,当我从 IntelliJ IDEA 运行应用程序而不是 Gradle 时,使用模仿 Gradle 的构建设置,一切正常,应用程序正常完成。

问题2

为什么 Gradle 挂起System.exit(0)而 IntelliJ 没有?这是否与 Gradle 本身是一个内部调用 Java 的进程以及在 IntelliJ 的情况下,Java 被立即调用而没有任何间接的事实有关?为什么这有关系?

问题 3

最终,我希望能够从 Gradle 运行它而没有任何挂起。的记录输出adb start-server将是一个奖励。我将不胜感激任何提示如何做到这一点。

4

2 回答 2

1

如果我回答错了问题,请原谅我,但如果我想从 gradle 启动服务器(我们正在为 tomcat、sonicMQ、ApacheDS 和 EnterpriseDB 执行此操作),我将从 gradle Exec 任务开始,然后只尝试其他的如果不够。

task startADB(type: Exec) {
    commandLine 'adb', 'start-server'
}

不幸的是,我无法回答您关于各种代码挂起的任何问题,因为我一无所知!我只知道如果我想从 gradle 启动服务器,我会这样做。事实上,我就是这样做的。另一个 - 稍微复杂一点 - 选项是使用tanuki 包装器或类似的东西。您仍然可以从 gradle 启动和停止它,但您可以获得一些高级功能。

于 2013-06-25T13:32:04.373 回答
0

最终我通过执行以下操作解决了这个问题:

  1. 在我的程序中,为了调用adb start-server,我不再使用org.apache.commons.exec.DefaultExecutor.execute("adb start-server")Java 的Runtime.getRuntime().exec("adb start-server").
    相反,我使用java.lang.ProcessBuilder("adb start-server").inheritIO().start().waitFor(). 请注意,它inheritIO()是在 Java 7 中添加的,并且允许在线读取已启动的进程标准输出等。
    编辑inheritIO()Gradle 直接从 CLI 调用而不是由 IntelliJ IDEA 调用时,in Gradle 无效。有关详细信息,请参阅此问题。可能按照此处StreamGobbler描述的方式实施可以解决问题。

  2. 在 Gradle 中,我再次使用重用任务变量,而不是 Gradle 的应用程序插件的任务。它看起来像这样:runProcessBuilderrun

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = '1.7'
targetCompatibility = '1.7'

// configuration-time of the original run task
run { 
    main = com.example.MainClass; // without this, our custom run will try to run "null"
}

task myRun(dependsOn: build) {

        // execution-time of our custom run task.
        doFirst {

            ProcessBuilder pb = new ProcessBuilder(tasks['run'].commandLine);
            pb.directory(tasks['run'].workingDir);

            // works when the gradle command is executed from IntelliJ IDEA, has no effect when executed from standalone CLI interface.
            pb.inheritIO(); 

            Process proc = pb.start();
            proc.waitFor();
        }
    }

至于奇怪行为的原因,我没有明确的答案,所以我不做进一步的评论。

编辑 2013 年 7 月 9 日
这个问题似乎指出问题 2 的答案是:在 Windows 上,父进程在终止之前等待子进程。不幸的是,由于问题 1 中所述的问题,那里提出的解决方案不起作用。

希望这会有所帮助,
康拉德

于 2013-06-25T17:50:18.463 回答