【问题标题】:SecondaryLoop in JavaFX, like Swing?JavaFX 中的 SecondaryLoop,如 Swing?
【发布时间】:2015-06-10 18:59:09
【问题描述】:

我有一个 Java Swing 应用程序,我正在研究它是否可以移植到 JavaFX。该应用程序是内部使用的脚本语言的开发环境和模拟器。有趣的是,您可以为这种脚本语言设置断点并逐步执行它,就像任何程序员对一种语言所期望的那样。

现在因为模拟器中的语言是解释器,在解释器执行的深处,当它遇到断点时,它可以使用 Java Swing SecondaryLoop 类弹回 gui。所以当断点被命中时,它会调用secondaryLoop.enter()。然后 gui 将被激活以供用户检查变量并且 gui 组件处于活动状态。当用户在程序中点击“继续”时,它会调用 secondaryLoop.exit() 以继续执行解释器。对于解释器来说,解开它的整个状态以返回到主循环,然后在完全相同的点从它停止的地方开始处理,这实际上是不可行的。这就是为什么 SecondaryLoop 在使其工作方面非常宝贵的原因。

这在 JavaFX 中可行吗?

【问题讨论】:

    标签: java swing javafx


    【解决方案1】:

    是的,这是可能的。您需要使用 enterNestedEventLoopexitNestedEventLoop 方法(它们位于 com.sun.javafx.tk.Toolkit 类中)。请参阅此用法示例:

    // Make sure to import the FX Toolkit first
    import com.sun.javafx.tk.Toolkit;
    
    // This object will be used as a unique identifier to the nested loop (to
    // block the execution of the thread until exitNestedEventLoop is called)
    final Object loopLock = new Object();
    
    // Simulate a long process thread (DB call, download, etc)
    Thread longProcess = new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            // Sleep for 12 seconds to simulate a long process
            try
            {
                Thread.sleep(12000);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
    
            // Setup a result to pass back to the enterNestedLoop() caller
            String result = "Result of this long process";
    
            // We are now done. Call exitNestedEventLoop() to unblock 
            // the enterNestedLoop() caller. This needs to run from
            // the FX Thread so use Platform.runLater()
            Runnable fxRunner = new Runnable()
            {
                public void run()
                {
                    try
                    {
                        Toolkit.getToolkit().exitNestedEventLoop(loopLock,
                                result);
                    } catch (Throwable t)
                    {
                        t.printStackTrace();
                    }
                }
            };
            Platform.runLater(fxRunner);
        }
    });
    
    // Start that long process from the FX Thread
    longProcess.start();
    // The next call will block until exitNestedEventLoop is called, however 
    // the FX Thread will continue processing UI requests
    Object result = Toolkit.getToolkit().enterNestedEventLoop(loopLock);
    // Next statement will print: "Result of this long process"
    System.out.println("Result is: " + result);
    

    现在,在您使用它之前警告两件重要的事情

    1. com.sun.javafx.tk.Toolkit 类不是公共 API 的一部分,因此 Oracle 保留删除它的权利,恕不另行通知。从 Java 7 到 8u51,我一直在使用它,所以它们可以永远留在那里,更改包/名称或完全消失(不太可能)。

    2. 嵌套循环(和 Swing 的辅助循环)非常适合灵活性和小型应用程序,但过度使用它们通常会付出代价。嵌套到许多循环(巨大的堆栈跟踪)通常会在您的应用程序中导致“奇怪”的行为,因为您的代码的初始部分可能最终会等待四五个与它们完全无关的事情。我已经看到 FX 嵌套循环导致 FX WebEngine executeScript() 调用中的“空”异常和重复键盘预处理(配对 FX+Swing 时)以及其他问题。

    也就是说,我建议使用 javafx.concurrent.Task 代替(如果有意义的话)。使用 Task 类需要更多的努力,但我认为这是正确的做事方式,可能会为您节省大量维护时间。

    有关 FX 任务类的额外参考,请参阅这篇精彩的文章:http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm

    更新:enterNestedEventLoopexitNestedEventLoop 将成为 Java 9 公共 API(平台类)的一部分,更多信息请参见 JDK-8090865

    希望这会有所帮助!

    【讨论】:

    • 根据 JEP 253:“随着 Java 9 的即将发布,特别是在 Project Jigsaw 中引入模块之间的强边界,开发人员会发现他们的代码将不再编译或运行,因为com.sun.* 包将无法再访问。” Also:“因此,如果以 com.sun.* 开头,则它不会存在于任何非 Oracle JDK 上。如果它属于其中一个包且未使用 @jdk.Exported 注释,则 Java 9 将无法访问它开。”
    • 感谢珠宝海提供的额外信息!事实上,这始终是使用 sun 包的风险。但是,我认为 FX 工具包最终将成为公共 API 的一部分,无论如何,如果有人有兴趣关注 see this openJDK request
    • 您的示例不起作用。 exitNestedEventLoop 抛出一个在平台线程上没有调用的异常。 :-/
    • @Vampire exitNestedEventLoop 和 enterNestedEventLoop 需要在 FX 线程中运行。您可以通过Platform.runLater() 调用来实现此目的
    • @JavierJ 我知道。但是您的代码没有这样做。你直接在一个新的Thread 中调用它,那是行不通的。 ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    • 1970-01-01
    • 2014-07-28
    • 2017-11-10
    • 1970-01-01
    • 2015-06-02
    • 2021-02-28
    相关资源
    最近更新 更多