基于自定义日志框架的解决方案
生成一个线程来执行您的命令,然后使用以下问题的答案:
这将以线程安全的方式将来自您的衍生线程启动的进程的消息记录回 UI。
如果您愿意,可以使用ExecutorService 来协助进行线程管理。如果你这样做了,记得彻底关闭它(通常在你的 JavaFX 应用程序的 stop() 方法中)。
基于 JavaFX 并发实用程序的解决方案
您也可以根据需要使用JavaFX concurrency utilities,例如Task、Service 和Platform.runLater。有关此方法的更多信息,请参阅Task java documentation 的“返回部分结果的任务”或“修改场景图的任务”部分。
使用 JavaFX 并发实用程序的示例。
import javafx.application.*;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ConsoleLogger extends Application {
@Override
public void start(Stage stage) throws Exception {
TextArea outputArea = new TextArea();
outputArea.setStyle("-fx-font-family: monospace");
outputArea.setEditable(false);
TextField commandField = new TextField();
commandField.setOnAction(event ->
executeTask(
commandField.getText(),
outputArea
)
);
VBox layout = new VBox(
10,
new Label("Enter a command and press return to execute it."),
commandField,
outputArea
);
VBox.setVgrow(outputArea, Priority.ALWAYS);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
private void executeTask(String commandString, TextArea outputArea) {
CommandExecutor commandExecutor = new CommandExecutor(
commandString,
outputArea
);
new Thread(commandExecutor).start();
}
public class CommandExecutor extends Task<Void> {
private final String commandString;
private final TextArea outputArea;
private final ProcessBuilder processBuilder;
public CommandExecutor(
String commandString,
TextArea outputArea
) {
this.commandString = commandString;
this.outputArea = outputArea;
processBuilder = new ProcessBuilder(
commandString.trim().split("\\s+")
)
.redirectErrorStream(true);
exceptionProperty().addListener((observable, oldException, newException) -> {
if (newException != null) {
newException.printStackTrace();
}
});
}
@Override
protected Void call() throws IOException {
Process process = processBuilder.start();
try (
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
process.getInputStream()
)
)
) {
String line;
while ((line = reader.readLine()) != null) {
final String nextLine = line;
Platform.runLater(
() -> outputArea.appendText(nextLine + "\n")
);
}
}
return null;
}
}
public static void main(String[] args) {
launch(args);
}
}
请注意,这是一个简单的解决方案,它可以淹没 JavaFX 可运行队列,不包括强大的异常处理,没有线程池,不处理采用交互式输入 I/O 的进程,将并发执行进程的输出合并到单个文本区域,不限制文本区域大小等。特别是,如果生成的进程执行诸如尾随不断快速写入的大型日志文件之类的操作,则需要小心,因为天真的解决方案可能多次调用Platform.runLater 淹没 JavaFX 可运行队列,这可不好。
对于更有效的解决方案,之前链接的自定义日志记录系统可能会更好。但是,对于某些应用程序,此示例中基于任务的系统记录到 TextArea 或对其进行一些小的修改可能没问题。
附加说明
在任何情况下,请注意不要尝试直接从另一个线程修改文本区域或其任何属性(使用Platform.runLater 来防止这种情况),否则程序可能会由于并发问题而失败。
在使用 Java Process API 时可能会发生很多技巧和意想不到的(不受欢迎的)事情,所以如果你不是很精通它,然后谷歌看看这些是什么以及如何解决它们如果您需要一个非常强大的解决方案。