JavaFX:文本区域中的控制台日志+;多线程和任务
我正试图实现一个许多人在StackOverflow上尝试过的盛宴:在基于JavaFX构建的TextArea中显示Java应用程序的控制台输出。JavaFX:文本区域中的控制台日志+;多线程和任务,java,multithreading,javafx,console,task,Java,Multithreading,Javafx,Console,Task,我正试图实现一个许多人在StackOverflow上尝试过的盛宴:在基于JavaFX构建的TextArea中显示Java应用程序的控制台输出。 我已经设法在上述文本区域中显示了我的输出,但和其他许多人一样,我的UI冻结了,因为这个线程正在大量加载用于显示UI本身的线程 因此,我开始阅读关于Platform.runLater(),但它并没有解决我的问题,主要是因为我输出了大量文本,这会减慢所述函数的速度。环顾四周,我进入了一个新的领域,提出了一个基于Task的解决方案。然而,一旦我开始在文本区域显
我已经设法在上述文本区域中显示了我的输出,但和其他许多人一样,我的UI冻结了,因为这个线程正在大量加载用于显示UI本身的线程 因此,我开始阅读关于
Platform.runLater()
,但它并没有解决我的问题,主要是因为我输出了大量文本,这会减慢所述函数的速度。环顾四周,我进入了一个新的领域,提出了一个基于Task
的解决方案。然而,一旦我开始在文本区域显示我的控制台日志,我的用户界面就会冻结。我将向您展示我的一段代码,这样您就可以告诉我我遗漏了什么和/或做错了什么
这是我的JavaFX控制器的一个片段:
public class MainViewController extends AbstractController implements Initializable {
@FXML private TextArea textAreaLog;
@Override
public void initialize(URL location, ResourceBundle resources) {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
Console console = new Console(textAreaLog);
PrintStream ps = new PrintStream(console, true);
System.setOut(ps);
System.setErr(ps);
return null;
}
@Override
protected void succeeded() {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
super.succeeded();
textAreaLog.textProperty().unbind();
}
};
textAreaLog.textProperty().bind(task.messageProperty());
new Thread(task).start();
}
// Console Class
public static class Console extends OutputStream {
private TextArea output;
Console(TextArea ta) {
this.output = ta;
}
@Override
public void write(int i) throws IOException {
output.appendText(String.valueOf((char) i));
}
}
}
公共类MainViewController扩展AbstractController实现可初始化{
@FXML私有文本区域文本区域日志;
@凌驾
公共void初始化(URL位置、ResourceBundle资源){
任务=新任务(){
@凌驾
受保护的Void调用()引发异常{
布尔值fxApplicationThread=Platform.isFxApplicationThread();
System.out.println(“在FXApplicationRead上调用:+FXApplicationRead”);
控制台=新控制台(文本区域日志);
PrintStream ps=新的PrintStream(控制台,真);
系统放样(ps);
系统设置错误(ps);
返回null;
}
@凌驾
受保护的无效已成功(){
布尔值fxApplicationThread=Platform.isFxApplicationThread();
System.out.println(“在FXApplicationRead上调用:+FXApplicationRead”);
super.successed();
textAreaLog.textProperty().unbind();
}
};
textAreaLog.textProperty().bind(task.messageProperty());
新线程(任务).start();
}
//控制台类
公共静态类控制台扩展OutputStream{
专用区域输出;
控制台(文本区域ta){
这个输出=ta;
}
@凌驾
公共无效写入(int i)引发IOException{
output.appendText(String.valueOf((char)i));
}
}
}
我已经编辑了我之前链接的问题的答案中的代码,留下了所有调试消息只是为了帮助我
就这些。我的UI只是冻结,即使我显然是在后台运行重载任务,而不是直接在UI线程中运行
是一种同步方法System.out.println(“text”)
- 在ui线程外部访问ui组件
System.out.println(“text”)
时,System.out上的同步将导致ui在同步期间冻结。
您可以检查这是否是如下所示的原因;(您必须像下面这样包装所有System.out调用,以便仅测试上述理论是否正确)这将导致
println
方法在不同线程中同步。(公共池线程)
您还应该更新ui线程中的输出组件。(在这种情况下,问题就解决了)
您向控制台/
stdout
写入内容的部分在哪里?既然任务
几乎立即返回,它有什么用途?而且,这不就是抛出一个异常吗?您可以绑定文本区域的textProperty()
,然后在仍然绑定的情况下,通过控制台和System.out.println(…)
在succeed()
方法中调用appendText()
。我的stdout被写在代码的四周,主要显示我从控制单元收到的电报。这是一个连续的任务,主要是因为控制单元不断向我发送新的信息。就我所见,我没有得到任何例外,除了我的UI冻结的事实。如果有一个合适的方法来实现我的目标,请告诉我!我不是Java方面的专业人士,我刚刚开始使用JavaFX。作为重定向系统输出的替代方法,我建议看看。如果底层需求必须是JavaFX中的系统输出重定向,那么也请参阅:@jewelsea,谢谢。我也会看看这些话题。现在我的代码正在运行,但是找到一个更好的方法重定向我的输出会很好。谢谢!我明天会告诉你的。关于“如果您没有使用Java8”的评论呢?目前我使用的是Java 9。我使用lambda将runnable传递给平台。runLater()
,如果您低于Java 8,则必须像新建runnable(…)
那样执行。好的,现在我明白了。为什么您认为在这行代码中使用runLater()是合适的?我已经读到,当你调用太多该方法的实例时,程序可能会开始变慢,最后冻结。在我的代码中,我正在输出一个连续的stdout流,我担心情况会是这样。我会让你知道的。谢谢你的帮助。@Davide3i据我所知,目前还没有线程安全的UI框架。他们要求仅从UI线程访问UI组件,因为他们无法管理从其他线程更改的UI组件的状态。这是UI框架的规则之一。如果您检查Swing的HelloWorld示例,它们都是从SwingUtilits.invokeLater()开始的。我认为他们在这方面做得不一样Fx@miroh,谢谢,罪犯没有使用Platform.runLater(()->output.appendText(String.valueOf((char)i))代码>。
CompletableFuture.runAsync(()->System.out.println("text"));
// or create new runnable if you are not using java8
Platform.runLater(()->output.appendText(String.valueOf((char) i)));