Swing JFrame 导致 JavaFX 应用程序在 OS X 上崩溃

Swing JFrame causes JavaFX application to crash on OS X

我正在处理一个复杂的 JavaFX 项目,该项目应该包括 Swing JFrames。一切正常,但 JFrames 在某些情况下拒绝关闭,从而锁定了整个 VM。 我已经将所有内容归结为一个最小的工作示例,它似乎可以在 Windows 上工作(请确认它是否在您的机器上工作)但可靠地崩溃 OS X。我正在使用 Java 8u25(最新稳定版)和 8u40 预览版(最新版本)- 没有区别。

如何重现:将程序另存为"JavaFXWithJFrames.java",编译并运行。现在有 3 个 Swing JFrames 和 1 个带有 3 个按钮的 JavaFX Window。单击按钮应分别关闭其中一个窗口。 这适用于 Windows(?) 但完全锁定 OS X 上的程序(可能还有其他 OSs?)

你能重现这个吗? Yes/No,哪个machine/OS/JRE?我在这里做错了什么 - 线程问题? 非常感谢您的帮助。

pastebin 上的代码:http://pastebin.com/tUrdNfCw# - 请另存为 "JavaFXWithJFrames.java" 进行编译!

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
//package javafxwithjframes;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javax.swing.JFrame;

/**
 * When starting, a JavaFX window and 3 Swing JFrame-windows appear.
 * The JavaFX window features 3 buttons that each are supposed to close one of the JFrames.
 * The program, however, freezes after clicking a button. What am I doing wrong?
 * It seems to work on Windows, but crash on OS X 10.10.1, using Java 8u25 (latest release) and Java 8u40 Dec 31 preview-build.
 *
 * @author a desperate developer
 */
public class JavaFXWithJFrames extends Application {

JFrame jframe1, jframe2, jframe3;

@Override
public void start(Stage primaryStage) {

    // Create 3 JFrames
    jframe1 = new JFrame("JFrame 1");
    jframe1.setBounds(50, 50, 200, 150);
    jframe1.setVisible(true);

    jframe2 = new JFrame("JFrame 2");
    jframe2.setBounds(275, 50, 200, 150);
    jframe2.setVisible(true);

    jframe3 = new JFrame("JFrame 3");
    jframe3.setBounds(500, 50, 200, 150);
    jframe3.setVisible(true);

    // Create 3 buttons that close each one JFrame
    // Button 1
    Button closeButton1 = new Button();
    closeButton1.setText("Close JFrame 1");
    closeButton1.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe1.setVisible(false);
            System.out.print("Closing JFrame 1...");
            jframe1.dispose();
        }
    });

    // Button 2
    Button closeButton2 = new Button();
    closeButton2.setText("Close JFrame 2");
    closeButton2.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe2.setVisible(false);
            System.out.print("Closing JFrame 2...");
            jframe2.dispose();
        }
    });

    // Button 3
    Button closeButton3 = new Button();
    closeButton3.setText("Close JFrame 3");
    closeButton3.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe3.setVisible(false);
            System.out.print("Closing JFrame 3...");
            jframe3.dispose();
        }
    });

    // Setting up main window
    HBox rootBox = new HBox();
    rootBox.getChildren().addAll(closeButton1, closeButton2, closeButton3);
    Scene scene = new Scene(rootBox, 400, 250);
    primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
        @Override
        public void handle(WindowEvent event) {
            System.exit(0);
        }
    });
    primaryStage.setTitle("JavaFX with JFrames");
    primaryStage.setScene(scene);
    primaryStage.show();
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

您在这里遇到了线程问题:您在 FX 应用程序线程上执行所有操作,而 Swing 工作应该在 AWT 事件调度线程上进行。

您可以使用 SwingUtilities.invokeLater(...); 在 AWT 事件处理线程上将代码安排到 运行。

所以你的代码应该是这样的

@Override
public void start(Stage primaryStage) {

    SwingUtilities.invokeLater( () -> {
        // Create 3 JFrames
        jframe1 = new JFrame("JFrame 1");
        jframe1.setBounds(50, 50, 200, 150);
        jframe1.setVisible(true);

        jframe2 = new JFrame("JFrame 2");
        jframe2.setBounds(275, 50, 200, 150);
        jframe2.setVisible(true);

        jframe3 = new JFrame("JFrame 3");
        jframe3.setBounds(500, 50, 200, 150);
        jframe3.setVisible(true);
    });

    // Create 3 buttons that close each one JFrame
    // Button 1
    Button closeButton1 = new Button();
    closeButton1.setText("Close JFrame 1");
    closeButton1.setOnAction(event -> {
        SwingUtilities.invokeLater(() -> {
            jframe1.setVisible(false);
            System.out.print("Closing JFrame 1...");
            jframe1.dispose();
        });
    });

    // similarly for other button handlers...

}

(我将您的事件处理程序更改为 lambda 表达式,因为嵌套的内部 类 太丑了,您说您使用的是 JDK 8...)

您也可以尝试 运行按原样调整您的应用程序,但使用实验系统 属性

-Djavafx.embed.singleThread=true

这 运行 是同一线程上的两个 UI 工具包。由于(据我所知)这仍然是实验性的,我建议将代码安排在 "correct" 线程上,如上所示。