【问题标题】:Modify JavaFX Application scene from different thread?从不同的线程修改 JavaFX 应用程序场景?
【发布时间】:2017-11-10 10:59:34
【问题描述】:

我已向我的客户端应用程序添加了一个 gui, 并尝试将套接字消息转发给它。 直到现在我还嘲笑客户是单身人士, 所以我可以从Application 线程访问它, 但现在我希望多个客户访问他们的 gui。 我知道fxml 绑定了属性监听器,但是 我希望它尽可能简单。 直接使用线程本身。 我如何让Platform.runLater() 引用 如果Application.launch() 构建自己的场景实例? 我已经阅读了相当广泛的内容,但到处都有解决方案 结果是利用fxml 界面跳过了我不喜欢的工作。 控制类(简化,第 9 行发布):

public class Client {
    private Socket socket = new Socket("127.0.0.1",4444);
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    private Browser browser = new Browser();
    public static void main(String[] arguments) throws IOException {
        Application.launch(browser.class);
        while(socket.isConnected()) {
            Platform.runLater(new Runnable(){
                @Override public void run(){
                    try{browser.bubble(incoming.readLine());}
                    catch(IOException fail){fail.printStackTrace();}
                }
            });
        }
    }
}

用户界面(简化):

public class Browser extends Application {
    private Stage stage;
    private Scene scene;
    private BorderPane layout = new BorderPane();
    private VBox history = new VBox();
    @Override public void start(Stage stage) throws Exception {
        layout.setCenter(history);
        scene = new Scene(layout);
        stage = stage;
        stage.setScene(scene);
        stage.show();
    }
    public void bubble(String message){
        VBox record = new VBox();
        Label label = new Label(message);
        record.getChildren().add(label);
        history.getChildren().add(record);
    }
}

【问题讨论】:

  • 在绝大多数情况下,您不应该从除 main 方法之外的任何地方调用 Application.launch(),也不应该在 main 方法中执行任何其他操作。反过来做; IE。从您的 start() 方法创建您的 Client 实例并调用包含您从那里发布的代码的方法(在后台线程中)。您仍然有线程问题 - 您正在使用 readLine() 调用阻塞 fx 应用程序线程。
  • 是的,对不起,我错过了包含 psvm 方法,已更正。我喜欢你绘制的原则,在主方法中没有其他内容并从应用程序实例化......将该实例分派到第三个线程也可以解决阻塞问题......我可能会这样下去。

标签: java multithreading user-interface javafx concurrency


【解决方案1】:

您应该只拥有一个Application 子类的实例,它是通过Application.launch() 启动应用程序时创建的实例。您在 JavaFX 应用程序中的 main() 方法实际上应该只调用 Application.launch() 并且什么都不做;您应该考虑将作为启动过程的一部分调用的start()(或init())方法作为应用程序的入口点。

因此,您应该从start() 方法创建Client 实例,并将其设置为在后台线程中执行的操作。

您可以通过如下重构代码来实现这一切:

public class Browser extends Application {
    private Stage stage;
    private Scene scene;
    private BorderPane layout = new BorderPane();
    private VBox history = new VBox();
    @Override public void start(Stage stage) throws Exception {
        layout.setCenter(history);
        scene = new Scene(layout);
        stage = stage;
        stage.setScene(scene);
        stage.show();

        Client client = new Client(this);
        Thread thread = new Thread(client::processIncomingData);
        thread.setDaemon(true);
        thread.start();
    }
    public void bubble(String message){
        VBox record = new VBox();
        Label label = new Label(message);
        record.getChildren().add(label);
        history.getChildren().add(record);
    }
    public static void main(String[] args) {
        Application.launch(args);
    }
}

public class Client {
    private Socket socket = new Socket("127.0.0.1",4444);
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    private Browser browser ;

    public Client(Browser browser) {
        this.browser = browser ;
    }

    public void processIncomingData()  {
        while(socket.isConnected()) {
            try {
                String data = incoming.readLine();
                Platform.runLater(() -> browser.bubble(data));
            } catch (IOException exc) {
                // TODO: handle properly
                exc.printStackTrace();
            }
        }
    }
}

还有几点需要注意:Application.launch() 阻塞,直到应用程序退出;因此,在您的原始代码中,您的 while 循环甚至不会在应用程序关闭之前启动。此外,readLine() 方法会阻塞,因此您不想在 FX 应用程序线程上执行此操作(它会阻止 UI 以任何方式响应,直到读取一行)。后一个问题已通过将 readLine() 移出 Platform.runLater() 块来解决。

【讨论】:

  • 好吧,所以我颠倒了逻辑并从应用程序线程实例化。这已经足够好了。在循环之前插入Application.launch() 只是我缩短的一个错误,但值得一提。谢谢你,詹姆斯。
猜你喜欢
  • 2015-05-28
  • 2020-09-30
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-30
相关资源
最近更新 更多