How to Call Launch() More Than Once in Java

How to call launch() more than once in java

You can't call launch() on a JavaFX application more than once, it's not allowed.

From the javadoc:

It must not be called more than once or an exception will be thrown.

Suggestion for showing a window periodically

  1. Just call Application.launch() once.
  2. Keep the JavaFX runtime running in the background using Platform.setImplicitExit(false), so that JavaFX does not shutdown automatically when you hide the last application window.
  3. The next time you need another window, wrap the window show() call in Platform.runLater(), so that the call gets executed on the JavaFX application thread.

For a short summary implementation of this approach:

  • See the answer by sergioFC

If you are mixing Swing you can use a JFXPanel instead of an Application, but the usage pattern will be similar to that outlined above.

  • For an example of the JFXPanel apprach, see Irshad Babar
    s answer.

Wumpus Sample

This example is bit more complicated than it needs to be because it also involves timer tasks. However it does provide a complete stand-alone example, which might help sometimes.

import javafx.animation.PauseTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.*;

// hunt the Wumpus....
public class Wumpus extends Application {
private static final Insets SAFETY_ZONE = new Insets(10);
private Label cowerInFear = new Label();
private Stage mainStage;

@Override
public void start(final Stage stage) {
// wumpus rulez
mainStage = stage;
mainStage.setAlwaysOnTop(true);

// the wumpus doesn't leave when the last stage is hidden.
Platform.setImplicitExit(false);

// the savage Wumpus will attack
// in the background when we least expect
// (at regular intervals ;-).
Timer timer = new Timer();
timer.schedule(new WumpusAttack(), 0, 5_000);

// every time we cower in fear
// from the last savage attack
// the wumpus will hide two seconds later.
cowerInFear.setPadding(SAFETY_ZONE);
cowerInFear.textProperty().addListener((observable, oldValue, newValue) -> {
PauseTransition pause = new PauseTransition(
Duration.seconds(2)
);
pause.setOnFinished(event -> stage.hide());
pause.play();
});

// when we just can't take it anymore,
// a simple click will quiet the Wumpus,
// but you have to be quick...
cowerInFear.setOnMouseClicked(event -> {
timer.cancel();
Platform.exit();
});

stage.setScene(new Scene(cowerInFear));
}

// it's so scary...
public class WumpusAttack extends TimerTask {
private String[] attacks = {
"hugs you",
"reads you a bedtime story",
"sings you a lullaby",
"puts you to sleep"
};

// the restaurant at the end of the universe.
private Random random = new Random(42);

@Override
public void run() {
// use runlater when we mess with the scene graph,
// so we don't cross the streams, as that would be bad.
Platform.runLater(() -> {
cowerInFear.setText("The Wumpus " + nextAttack() + "!");
mainStage.sizeToScene();
mainStage.show();
});
}

private String nextAttack() {
return attacks[random.nextInt(attacks.length)];
}
}

public static void main(String[] args) {
launch(args);
}
}

Update, Jan 2020

Java 9 added a new feature called Platform.startup(), which you can use to trigger startup of the JavaFX runtime without defining a class derived from Application and calling launch() on it. Platform.startup() has similar restrictions to the launch() method (you cannot call Platform.startup() more than once), so the elements of how it can be applied is similar to the launch() discussion and Wumpus example in this answer.

For a demonstration on how Platform.startup() can be used, see Fabian's answer to How to achieve JavaFX and non-JavaFX interaction?

Is it possible to call a JavaFX application more than once in a Java application?

Assuming that you are using Swing in your Java application, to make use of JavaFX, you would not use the application class itself, but rather do the things you do in the Applications start method to construct a JavaFX scenegraph and embed this scene graph inside a Swing JFXPanel.

I made a small example:

import javafx.animation.FadeTransition;
import javafx.animation.RotateTransition;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Swing2Test extends JFrame {

public Swing2Test() {

setTitle("Simple example");

JPanel panel = new JPanel();
getContentPane().add(panel);

JButton button = new JButton("Show JavaFX Dialog");
button.setBounds(50, 60, 80, 30);

button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
final JDialog dialog = new JDialog(Swing2Test.this,
"JavaFX Dialog",
true);
final JFXPanel contentPane = new JFXPanel();
dialog.setContentPane(contentPane);
dialog.setDefaultCloseOperation(
JDialog.HIDE_ON_CLOSE);

// building the scene graph must be done on the javafx thread
Platform.runLater(new Runnable() {
@Override
public void run() {
contentPane.setScene(createScene());

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
dialog.pack();
dialog.setVisible(true);
}
});
}
});
}

private Scene createScene() {
System.out.println("creating scene");
StackPane root = new StackPane();
Rectangle rect1 = new Rectangle(10, 10, 50, 50);
rect1.setFill(Color.BLUE);
Rectangle rect2 = new Rectangle(0, 0, 30, 30);
rect2.setFill(Color.ORANGE);
root.getChildren().addAll(rect1, rect2);
FadeTransition ft = new FadeTransition(Duration.millis(1800), rect1);
ft.setFromValue(1.0);
ft.setToValue(0.1);
ft.setCycleCount(Timeline.INDEFINITE);
ft.setAutoReverse(true);
ft.play();
RotateTransition rt = new RotateTransition(Duration.millis(1500), rect2);;
rt.setByAngle(180f);
rt.setCycleCount(Timeline.INDEFINITE);
rt.setAutoReverse(true);
rt.play();
return new Scene(root, 150, 150);
}
});

panel.add(button);

setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Swing2Test ex = new Swing2Test();
ex.setVisible(true);
}
});
}
}

Call JavaFX application twice

You can't launch a JavaFX application more than once in the same process, so don't try to do that.

You need to find an alternative mechanism to do whatever it is you are trying to do.

If you are embedding JavaFX scenes in a Swing application you should be creating new JFXPanels in Swing, not creating new JavaFX Applications on Swing button presses.

If you are intending to have a pure JavaFX application, then there is no need to have a Swing button which launches a JavaFX application, you can just use a JavaFX button instead and directly display the JavaFX scene.

There is no need to use a Service in this case, Service is used for performing repeated background tasks on a another thread, which has nothing to do with what you are attempting.

Read JavaFX for Swing developers if you want to integrate a Swing and JavaFX application.

JavaFX launch another application

Application is not just a window -- it's a Process. Thus only one Application#launch() is allowed per VM.

If you want to have a new window -- create a Stage.

If you really want to reuse anotherApp class, just wrap it in Platform.runLater()

@Override
public void update(Observable o, Object arg) {
Platform.runLater(new Runnable() {
public void run() {
new anotherApp().start(new Stage());
}
});
}

How to call a JAVA FX Class from another class many times

As suggested in the comments, don't use the predefined application lifecycle defined by the Application class. Just make the UI a regular Java class:

import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;

public class Ui {

private final Pane view ;

public Ui() {
view = new StackPane();
Button button = new Button("Close");
button.setOnAction(e -> button.getScene().getWindow().hide());
view.getChildren().add(button);
}

public Parent getView() {
return view ;
}
}

and then start the JavaFX platform "by hand", and show a window as needed. You just need to take a little care here to ensure that UI tasks are performed on the FX Application Thread.

You probably want to wait to prompt the user again until the window is closed. I use a CountDownLatch for this. If you want the prompt to appear again immediately (so the user can have multiple windows open at the same time), just omit that logic.

import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App {

public static void main(String[] args) {

// Start the JavaFX Platform:
Platform.startup(() -> {});

// Don't exit JavaFX platform when the last window closes:
Platform.setImplicitExit(false);

try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.println("Enter choice (1=test, 2=Show UI, 3=Exit):");
switch(scanner.nextLine()) {
case "1":
test();
break ;
case "2":
showUI();
break ;
case "3":
System.exit(0);
default:
System.out.println("Invalid option");
}
}
}
}

private static void test() {
System.out.println("Test");
}

private static void showUI() {

// latch to indicate window has closed:
CountDownLatch latch = new CountDownLatch(1);

// create and show new window on FX Application thread:
Platform.runLater(() -> {
Stage stage = new Stage();
Ui ui = new Ui();
Scene scene = new Scene(ui.getView(), 600, 600);
stage.setScene(scene);

// signal latch when window is closed:
stage.setOnHidden(e -> latch.countDown());

// show window:
stage.show();
});
try {
// Wait for latch (i.e. for window to close):
latch.await();
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
}

}

Reflected Method calling constructor multiple times

Application.launch() creates an instance of the class on which it was called (necessarily a subclass of Application). That's where the second instance is coming from.



Related Topics



Leave a reply



Submit