【问题标题】:JavaFX Application: is the primary stage in .start() any special?JavaFX 应用程序:.start() 中的主要阶段有什么特别之处吗?
【发布时间】:2015-03-14 06:12:15
【问题描述】:

这是基本的 fxml 文件:

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
    minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
    xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="com.github.parboiled1.grappa.debugger.basewindow.BaseWindowUi">
    <top>
        <MenuBar BorderPane.alignment="CENTER">
            <Menu mnemonicParsing="false" text="File">
                <MenuItem mnemonicParsing="false"
                    text="New window" onAction="#newWindowEvent"/>
                <MenuItem mnemonicParsing="false"
                    text="Close" onAction="#closeWindowEvent"/>
            </Menu>
        </MenuBar>
    </top>
    <center>
        <Label text="Hello"/>
    </center>
</BorderPane>

BaseWindowUi 类很简单:

public class BaseWindowUi
{
    private BaseWindowPresenter presenter;

    public void init(final BaseWindowPresenter presenter)
    {
        this.presenter = presenter;
    }

    @FXML
    void newWindowEvent(final ActionEvent event)
    {
        presenter.handleNewWindow();
    }

    @FXML
    public void closeWindowEvent(final ActionEvent event)
    {
        presenter.handleCloseWindow();
    }
}

主持人也是:

public final class BaseWindowPresenter
{
    private final BaseWindowFactory windowFactory;

    public BaseWindowPresenter(final BaseWindowFactory windowFactory)
    {
        this.windowFactory = windowFactory;
    }

    public void handleCloseWindow()
    {
        windowFactory.close(this);
    }

    public void handleNewWindow()
    {
        windowFactory.createWindow();
    }
}

BaseWindowFactory 是一个只有一个具体实现的接口:

// Interface...
public interface BaseWindowFactory
{
    void createWindow(Stage stage);

    default void createWindow()
    {
        createWindow(new Stage());
    }

    void close(BaseWindowPresenter presenter);
}

// Implementation...
public final class DefaultBaseWindowFactory
    implements BaseWindowFactory
{
    private static final AlertFactory ALERT_FACTORY = new AlertFactory();
    private static final URL MAIN_WINDOW_FXML;

    static {
        try {
            MAIN_WINDOW_FXML = DefaultBaseWindowFactory.class.getResource(
                "/baseWindow.fxml");
            if (MAIN_WINDOW_FXML == null)
                throw new IOException("base window fxml not found");
        } catch (IOException e) {
            ALERT_FACTORY.showError("Fatal error", "cannot load base FXML", e);
            throw new ExceptionInInitializerError(e);
        }
    }

    private final Map<BaseWindowPresenter, Stage> windows
        = new HashMap<>();

    private final AtomicInteger windowCount = new AtomicInteger();

    @Override
    public void createWindow(final Stage stage)
    {
        final FXMLLoader loader = new FXMLLoader(MAIN_WINDOW_FXML);
        final Pane pane;
        try {
            pane = loader.load();
        } catch (IOException e) {
            ALERT_FACTORY.showError("Fatal error", "unable to create window",
                e);
            return;
        }

        final BaseWindowPresenter presenter = new BaseWindowPresenter(this);

        final BaseWindowUi ui = loader.getController();
        ui.init(presenter);

        stage.setScene(new Scene(pane));
        stage.setTitle("window " + windowCount.getAndIncrement());

        windows.put(presenter, stage);

        stage.show();
    }

    @Override
    public void close(final BaseWindowPresenter presenter)
    {
        windows.get(presenter).close();
    }
}

最后,这是实现Application的类:

public final class TestApp
    extends Application
{
    private final BaseWindowFactory factory = new DefaultBaseWindowFactory();

    @Override
    public void start(final Stage primaryStage)
        throws Exception
    {
        factory.createWindow(primaryStage);
    }

    public static void main(final String... args)
    {
        launch(args);
    }
}

所有这些工作;我可以打开新窗口,关闭从“初级阶段”创建的窗口,关闭所有窗口时应用程序正确退出等等。

那么,初级阶段有什么特别之处呢?

更重要的是,Application 的文档是这样说的:

JavaFX 创建一个应用程序线程,用于运行应用程序启动方法、处理输入事件和运行动画时间轴。 JavaFX 场景和舞台对象的创建以及将场景图操作修改为活动对象(那些已经附加到场景的对象)必须在 JavaFX 应用程序线程上完成。

我的代码目前可以工作,但它几乎什么也没做;我最终不会遇到应该在应用程序线程中运行但没有运行的代码问题吗?所有new Stage()s 都自动知道应用程序线程吗?

【问题讨论】:

  • 您的具体问题是什么?请编辑标题以包含问题并编辑您的问题以清楚地询问您需要知道的内容。当我无法完全理解您不理解的内容时,要帮助您理解某些内容有点困难。为什么你认为初级阶段很特别?为什么你认为这段代码会遇到线程问题?为什么使用 AtomicInteger 进行窗口计数?你要求什么规则?
  • @jewelsea 问题已编辑;问题是,我什至不知道完全正确地表达我的问题......
  • 您的应用程序不会创建任何额外的线程。这意味着一切都已经是应用程序线程的一部分,无需担心线程。

标签: java javafx javafx-8


【解决方案1】:

实际上有一个(重要的非显而易见的)区别:用户可以通过单击窗口装饰中的最小化按钮来最小化初级阶段。对于其他阶段,窗口最小化按钮被禁用。我们必须以不同的方式设计我们的应用来解决这个问题。

使用 Java 1.8.0_121 测试。

【讨论】:

【解决方案2】:

初级阶段

primaryStage 唯一的特别之处在于(与任何其他阶段不同),它是由 JavaFX 系统创建的,而不是由您的应用程序创建的。但除此之外,初级阶段的行为与其他阶段一样。

有一个与application lifecycle相关的阶段相关规则:

  • 等待应用程序完成,当发生以下任一情况时会发生这种情况:
    • 应用程序调用Platform.exit() OR
    • 最后一个窗口已关闭,Platform 上的implicitExit 属性为真

因此,如果您从未在应用程序中显示primaryStage(或任何其他阶段)并且您拥有默认的implicitExit 设置,那么您的应用程序将永远不会退出,因此请务必调用primaryStage.show()。

也许初级阶段对您来说似乎很特别,因为它恰好是您要关闭的最后一个窗口,因此应用程序在关闭后会自动退出。但是,如果其他窗口是最后一个关闭的窗口,应用程序将同样退出,因此初级阶段在这方面并没有什么特别之处。

线程

实际上只有一个与线程相关的规则,并且您已经在问题中引用了它:

  • 必须在 JavaFX 应用程序线程上创建 JavaFX 场景和舞台对象以及将场景图操作修改为活动对象(那些已附加到场景的对象)。

您提供的代码没有引入任何新线程,它只是使用了 JavaFX 系统(JavaFX 应用程序线程)传递给它的线程。因此,您提供的代码永远不会违反上述规则(正如 Vitomir 在他的评论中指出的那样)。

您的 DefaultBaseWindowFactory createWindow 方法应该只在 JavaFX 应用程序线程上运行,因此您不需要诸如 AtomicInteger 之类的并发实用程序来封装窗口的数量,一个简单的整数就足以作为唯一的线程来读取或写入value 将在 JavaFX 应用程序线程上运行。

您是否应该引入创建新线程的新代码(您编写的代码或库中的代码),然后注意此代码不会修改活动场景图中的对象,也不要尝试直接从另一个创建窗口线程(如果有疑问,您可以通过System.out.println(Thread.getName() 轻松检查您正在执行的线程)。如果您最终拥有一些多线程代码,请使用 Platform.runLater() 包装对在 SceneGraph 中操作节点或创建新窗口的函数的任何调用,或使用 JavaFX concurrency utilities 管理并发服务。

【讨论】:

  • 初级阶段有些特殊,尽管可能不是问题所问的方式。它不能有所有者,不能设置其模态,也不能以showAndWait() 显示。
猜你喜欢
  • 2013-03-26
  • 1970-01-01
  • 2013-05-11
  • 2014-12-24
  • 1970-01-01
  • 1970-01-01
  • 2011-07-13
  • 1970-01-01
  • 2011-04-18
相关资源
最近更新 更多