underkuerbis
underkuerbis

Reputation: 335

Swing JFrame causes JavaFX application to crash on OS X

I'm working on a complex JavaFX project that is supposed to include Swing JFrames. Everything works fine, the JFrames however refuse to close under certain conditions, locking up the entire VM. I've boiled everything down to a minimum working example, which seems to work on Windows (please confirm if it works on your machine) but crashes OS X reliably. I'm using Java 8u25 (latest stable) and 8u40 preview (latest build) - no difference.

How to reproduce: Save the program as "JavaFXWithJFrames.java", compile & run it. There are now 3 Swing JFrames and 1 JavaFX Window with 3 Buttons. Clicking the Buttons should close 1 of the windows, respectively. This works on Windows(?) but completely locks up the program on OS X (and maybe other OSs?)

Can you reproduce this? Yes/No, Which machine/OS/JRE? What am I doing wrong here - threading issues? Thanks so much for your help.

Code on pastebin: http://pastebin.com/tUrdNfCw# - please save as "JavaFXWithJFrames.java" to compile!

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

}

Upvotes: 3

Views: 1307

Answers (1)

James_D
James_D

Reputation: 209339

You have a threading issue here: you are doing everything on the FX Application Thread, whereas the Swing work should be on the AWT event dispatch thread.

You can schedule code to run on the AWT event handling thread using SwingUtilities.invokeLater(...);.

So your code should look like

@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...

}

(I changed your event handler to a lambda expression as the nested inner classes are simply too ugly, and you said you were using JDK 8...)

You can also try running your application as it is, but with the experimental system property

-Djavafx.embed.singleThread=true

This runs both UI toolkits on the same thread. Since (to the best of my knowledge) this is still experimental, I would recommend scheduling the code on the "correct" threads as shown above.

Upvotes: 6

Related Questions