【问题标题】:JavaFX: Console Log in a TextArea + Multithreading and TasksJavaFX:文本区域中的控制台日志 + 多线程和任务
【发布时间】:2018-02-20 15:50:13
【问题描述】:

我正在尝试实现许多人在 StackOverflow 上尝试过的盛宴:在基于 JavaFX 构建的 TextArea 中显示 Java 应用程序的控制台输出。
我已经设法在所述 TextArea 中显示了我的输出,但是和其他许多人一样,我的 UI 冻结了,因为这个线程正在大量加载用于显示 UI 本身的线程。

所以我开始阅读有关Platform.runLater() 的内容,但这并不能解决我的问题,主要是因为我输出了大量文本,这会减慢所述功能。环顾四周,我进入了this question,其中提出了一个基于Task 的不错的解决方案。尽管如此,一旦我开始将控制台日志显示到 TextArea 中,我的 UI 就会一直冻结。我将向您展示我的代码的 sn-p,以便您可以告诉我我缺少什么和/或做错了什么。

这是我的 JavaFX 控制器的 sn-p:

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));
        }

    }

}

我已经编辑了从我之前链接的问题的答案中提取的代码,留下所有调试消息只是为了帮助我。

就是这样。我的 UI 只是冻结了,即使我显然是在后台运行我的繁重任务而不是直接在我的 UI 线程中执行此操作。

【问题讨论】:

  • 你写东西到控制台/stdout的部分在哪里?由于Task 几乎立即返回,它有什么用途?
  • 另外,这不是抛出异常吗?你绑定文本区域的textProperty(),然后,当它仍然被绑定时,在succeeded()方法中你通过控制台调用appendText()System.out.println(...)
  • 我的标准输出是围绕代码编写的,主要显示我从控制单元获得的电报。这是一项持续的任务,主要是因为所述控制单元不断向我发送新信息。就我所见,我没有得到任何异常,除了我的 UI 冻结的事实。如果有适当的方法来实现我想要做的事情,请随时告诉我!我不是 Java 专家,我刚刚开始使用 JavaFX。
  • 作为重定向系统输出的替代方法,我建议查看Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks。如果底层需求必须是 JavaFX 中的系统输出重定向,那么另请参阅:Replicating console functionality with a ListView
  • @jewelsea,谢谢。我也会看看这些主题。现在我的代码可以工作了,但是找到一个更好的方法来重定向我的输出会很好。

标签: java multithreading javafx console task


【解决方案1】:

我认为问题的根源是以下之一;

  • System.out.println("text") 是一种同步方法。
  • 在 Ui 线程之外访问 ui 组件

当您从 ui 线程调用 System.out.println("text") 时,System.out 上的同步将导致 UI 在同步期间冻结。 您可以检查这是否是如下原因;(您必须像下面这样包装所有 System.out 调用,仅用于测试上述理论是否正确)
这将导致println 方法在不同的线程中同步。(公共池线程)

CompletableFuture.runAsync(()->System.out.println("text"));

您还应该更新 ui 线程中的输出组件。(在这种情况下问题已解决)

    // or create new runnable if you are not using java8
    Platform.runLater(()->output.appendText(String.valueOf((char) i))); 

【讨论】:

  • 谢谢!明天我会告诉你的。关于“如果您不使用 Java 8”的评论呢?目前我使用的是 Java 9。
  • 我使用 lambda 将 runnable 传递给 Platform.runLater() ,如果你低于 java 8,你可以像 new Runnable(...) 那样做。
  • 好的,现在我明白了。为什么您认为在该行代码上使用 runLater() 是正确的?我已经读过,当您调用该方法的太多实例时,程序可能会开始变得迟钝,并最终冻结。在我的代码中,我正在输出连续的 stdout 流,恐怕会是这种情况。我会告诉你。感谢您的帮助。
  • @Davide3i 据我所知,不存在线程安全的 UI 框架。他们要求仅从 UI 线程访问 UI 组件,因为他们无法管理从其他线程更改的 UI 组件的状态。这是 UI 框架的规则之一。如果您甚至查看 HelloWorld 的 Swing 示例,它们都是从 SwingUtilits.invokeLater() 开始的。我不认为他们可以在 Fx 中做不同的事情
  • @miroh,谢谢,罪魁祸首没有使用Platform.runLater(()-&gt;output.appendText(String.valueOf((char) i)));
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多