【问题标题】:How to call launch() more than once in java如何在java中多次调用launch()
【发布时间】:2014-06-20 04:33:25
【问题描述】:

如何在 java 中多次调用 launch() 我得到一个异常为“ERROR IN MAIN:java.lang.IllegalStateException: Application launch must not be called more than once”

当请求到来时,我在我的 java 应用程序中创建了 rest cleint,它调用 javafx 并在完成 webview 操作后打开 webview,我正在使用 Platform.exit() 方法关闭 javafx 窗口。当第二个请求到来时,我收到此错误如何解决此错误。

JavaFx 应用程序代码:

public class AppWebview extends Application  {

    public static Stage stage;

    @Override
    public void start(Stage _stage) throws Exception {

        stage = _stage;
        StackPane root = new StackPane();

        WebView view = new WebView();

        WebEngine engine = view.getEngine();
        engine.load(PaymentServerRestAPI.BROWSER_URL);
        root.getChildren().add(view);
        engine.setJavaScriptEnabled(true);
        Scene scene = new Scene(root, 800, 600);
        stage.setScene(scene);

        engine.setOnResized(new EventHandler<WebEvent<Rectangle2D>>() {
            public void handle(WebEvent<Rectangle2D> ev) {
                Rectangle2D r = ev.getData();
                stage.setWidth(r.getWidth());
                stage.setHeight(r.getHeight());
            }
        });

        JSObject window = (JSObject) engine.executeScript("window");
        window.setMember("app", new BrowserApp());

        stage.show();

    }

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

RestClient 方法: 调用 JavaFX 应用程序

// method 1 to lanch javafx
javafx.application.Application.launch(AppWebview.class);

// method 2 to lanch javafx
String[] arguments = new String[] {"123"};
AppWebview .main(arguments);

【问题讨论】:

    标签: java javafx javafx-webengine


    【解决方案1】:

    您不能在 JavaFX 应用程序上多次调用 launch(),这是不允许的。

    来自 javadoc:

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

    定期显示窗口的建议

    1. 只需拨打一次Application.launch()。
    2. 使用 Platform.setImplicitExit(false) 保持 JavaFX 运行时在后台运行,这样当您隐藏最后一个应用程序窗口时 JavaFX 不会自动关闭。
    3. 下次您需要另一个窗口时,将窗口show() 调用包装在Platform.runLater() 中,以便在JavaFX 应用程序线程上执行调用。

    对于此方法的简短摘要实现:

    如果您正在混合 Swing,您可以使用 JFXPanel 而不是 Application,但使用模式将与上面概述的相似。

    Wumpus 样本

    这个例子比它需要的要复杂一些,因为它还涉及定时器任务。但是,它确实提供了一个完整的独立示例,有时可能会有所帮助。

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

    2020 年 1 月更新

    Java 9 添加了一个名为Platform.startup() 的新功能,您可以使用它来触发JavaFX 运行时的启动,而无需定义派生自Application 的类并在其上调用launch()。 Platform.startup() 与 launch() 方法有类似的限制(您不能多次调用 Platform.startup()),因此如何应用它的元素类似于此答案中的 launch() 讨论和 Wumpus 示例。

    有关如何使用 Platform.startup() 的演示,请参阅 Fabian 对 How to achieve JavaFX and non-JavaFX interaction? 的回答

    【讨论】:

    • @jewelsea 你能给出这个示例代码吗,对于完成的请求过程,我想关闭窗口。我已将此集成到我的 spring mvc 项目中。请给我示例代码。
    • @jewelsea 请给我发送此问题的示例代码。谢谢
    • @jewelsea 非常感谢您发布答案。但这对我来说不是有效的答案。我已经实现了 spring mvc 应用程序并托管在我的服务器中。当服务器启动时,我也想启动 java fx 应用程序。我的一个spring模块将调用java fx应用程序传递url,然后java fx我需要在webview中显示该网页,在处理该页面后,我需要将网页输出发送回spring mvc mobule,直到获得输出表单网页spring将等待然后,我只需要使用一个线程来完成 javafx 功能。
    • 我的 Spring 应用程序希望在任何时间点都可以完全访问 javafx 应用程序的控制权,非常感谢
    • Wumpus 的例子既有用又有趣。谢谢!
    【解决方案2】:

    我使用类似的东西,类似于其他答案。

    private static volatile boolean javaFxLaunched = false;
    
    public static void myLaunch(Class<? extends Application> applicationClass) {
        if (!javaFxLaunched) { // First time
            Platform.setImplicitExit(false);
            new Thread(()->Application.launch(applicationClass)).start();
            javaFxLaunched = true;
        } else { // Next times
            Platform.runLater(()->{
                try {
                    Application application = applicationClass.newInstance();
                    Stage primaryStage = new Stage();
                    application.start(primaryStage);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }
    

    【讨论】:

    • 非常感谢塞尔吉奥。我会看看它。我可以知道你每次调用 myLaunch 方法时是如何传递参数的吗?
    • 我不需要传递参数。您可以使用Application.launch(applicationClass, args) 而不是Application.launch(applicationClass),但我认为您不能在 Platfor.runLater 中传递参数
    • 感谢您的更新!我们是否必须在应用程序中显式调用 Platform.exit() 才能停止它?
    • 非常感谢!这个例子简单明了。
    【解决方案3】:

    试试这个,我试过这个,发现成功了

    @Override
    public void start() {
        super.start();
        try {
                        // Because we need to init the JavaFX toolkit - which usually Application.launch does
                        // I'm not sure if this way of launching has any effect on anything
            new JFXPanel();
    
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    // Your class that extends Application
                    new ArtisanArmourerInterface().start(new Stage());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    【讨论】:

    • 谢谢。在我的场景中,我使用Platform.setImplicitExit(false) + Application.launch(ApplicationX.class) 解决了它以启动第一个应用程序 + 你的Platform.runLater 用于我以后使用的应用程序。这样我就不需要使用new JFXPanel();
    • 抱歉,这段代码属于哪个类?在AppWebview?
    • @Nithin 你的意思是Application.launch(myClass) 的参数吗?也许您可以使用以前的一些静态方法,例如MyClass.staticMethodToInit(value1, value2)。
    • @Nithin 我不确定我是否理解这与您的问题有什么关系。您仍然可以将参数传递给 launch 方法。
    猜你喜欢
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多