避免不在FX应用程序线程上会导致UI崩溃

时间:2018-10-17 22:47:48

标签: java multithreading javafx

我正在使用JavaFX逐步开发游戏,并且正在使用do while循环来等待用户输入。问题是,它是从fxml控制器的initialize方法调用的,这意味着该接口永远不会加载。解决方案是等待使用另一个线程,但是JavaFX不允许修改辅助线程中的任何节点。

我避免通过使用以下代码获得“不在FX应用程序线程上”:

public void initialize(URL arg0, ResourceBundle arg1) {
        // I call the game engine to start the game sequence and I pass root into it for further use
        engine = new GameEngine(root);

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                engine.start();
            }
          });
    }

在engine.start()方法内部:

public void start() {
        System.out.println("Game started");
        text("Hello, stranger"); // a new Label inside VBox
        do {
            try {
                Thread.sleep((long) 1000);
                System.out.println("Sleeping...");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                System.out.println("Error... waiting?");
            }
        } while(this.listener == false); // Listener triggers when user inputs.

        System.out.println("ENTER PRESSED, May proceed");
        text("It worked!");
    }

此外,GameEngine扩展了Thread,我不知道它如何影响多线程之外的任何事物。

启动代码后,UI崩溃,输出使Sleeping ...每秒都应有的跳动。

2 个答案:

答案 0 :(得分:1)

您不能在JavaFX Application Thread上调用Thread.sleep()和/或while循环,因为这将导致GUI停止并等待循环结束。

  

解决方案是等待使用另一个线程,但是JavaFX不允许   从辅助线程修改任何节点。

是的,您不应该从非JavaFX Application Threads修改场景图,但这不会阻止您为进行实际修改的代码行调用Platform.runLater()。您也可以使用Task来实现这一点。

很难提供任何具体的解决方案,因为我不知道listener标志是如何更新的。

编辑

在fabian指出之前,我没有注意到start()方法属于Thread子类。您永远都不要覆盖Thread.start(),并且更糟糕的是,不要在覆盖它时调用super.start()

因此,这里发生的是您的GameEngine类不再像Thread那样工作。调用engine.start()只会在调用了线程engine.start()的情况下执行您在覆盖中编写的任何代码(恰好是JavaFX Application Thread,因为您已将其包装在Platform.runLater()中)。 / p>

如果您仍然希望扩展Thread,则应移动代码以覆盖Thread.run()。另外请注意,您应该删除Platform.runLater()中的initialize(),并将其放置在任何会影响GUI的代码中。

例如:

Platform.runLater(() -> {
    myLabel.setText("User typed" + input);
});

答案 1 :(得分:0)

基本上,使用多线程必须是明智的。一个线程等待主线程编辑布局。放置

Platform.runLater(() -> {
  // layout manipulations 
});

围绕布局的操作可以在主线程之外按预期工作。