JavaFX 阻止 UI(和代码)直到服务器调用完成

标签 java swing user-interface javafx blocking

这个主题存在于无数“在长时间持续调用耗时的内容(服务器)期间不阻塞 JavaFX UI”的文章中。我知道这一点,我用谷歌搜索并尝试了很多。 大多数“互联网”解释说,由JavaFX事件引起的持久调用需要在额外的线程中进行。线程完成后,您可以通过 Platform.runLater() 使用结果更新 FX UI。大多数 FX 库都用复杂的代码结构封装了这一点(非常酷的东西)。 我当前的问题是:我们正在将 Swing 富客户端迁移到 JavaFX。它是一个巨大的客户端,因此我们必须不断地在其中包含/替换 JavaFX UI,直到它成为一个完整的 FX 客户端。 客户端中有一些功能会执行服务器调用,并且必须等待用户才能继续其工作。 服务器使用带有无状态 session bean 和接口(interface)的 JEE6。这些接口(interface)对于客户端来说是已知的,并且通过我们自己的一个小库,我们实现了一个小代理,对客户端隐藏了通信层。 客户端只需使用库创建一个“RemoteProxy”,然后调用远程接口(interface),库将调用传播到服务器。调用该方法并将结果或异常传输回客户端。对于客户端来说,这看起来就像本地调用。 问题就在这里。典型的代码片段如下所示:

...
ServerRemoteInterface myServerRemoteProxy = Helper.getProxyForInterface(ServerRemoteInterface.class) ;
...
ComplexResult result = myServerRemoteProxy.doThingsOnServer(SerializableParameter p1 ...)
doSomethingUsefull() ;

对服务器的调用在 Swing UI 线程中触发(通过监听器)。它停止程序(监听器代码)的执行,尽管它是在额外的线程中完成的。服务器返回后调用“doSomethingUsefull()”。开发人员不必关心这里的线程。 它是如何实现的?通过使用“旋转库”(http://spin.sourceforge.net/)。 它对 Swing EDT 做了一些巧妙的处理。 另一种选择是使用模态对话框,但我们决定不使用额外的窗口,而是使用玻璃 Pane 来禁用一些 UI 组件。

这么长的解释和简短的问题...... JavaFX 是否有类似的东西可以帮助我们无缝调用服务器、停止程序执行直到它返回并且不阻塞 JavaFX UI?如果它可以与 Java Swing 部分代码一起工作那就最好了。

编辑...添加一个非常压缩的示例来演示隐藏 JDialog 的使用。

我们需要服务器远程接口(interface)。任何接口(interface)都可以。

public interface ServerRemoteInterface
{
   public String method1() ; // String result in our case for test purposes
   public String method2() ; // Exceptions would also be possible.
}

然后我们需要代理调用处理程序

public class ServerProxy implements InvocationHandler
{
   public Object result;
   JDialog modalDialog = new JDialog((Frame)null, true);

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
      ServerCallHelper serverThread = new ServerCallHelper(args, method) ;
      modalDialog.setLocation(4000, 5000); // as if using Spin library. Here we use the dialog to block in a location off the screen.
      serverThread.start();
      modalDialog.setVisible(true) ;
      return result;
   }

   class ServerCallHelper extends Thread
   {
      Object[] args;
      Method method;

      public ServerCallHelper(Object[] args, Method method)
      {
         this.args = args;
         this.method = method;
      }

      public void run()
      {
         // do a server call via rmi or tunnel it via http, REST or whatever and provide the call parameters. On the server side there must be a receiver propagating the call to the wanted session bean.
         // here we just do a simulation
         try
         {
            Thread.sleep(3000);
         } catch (InterruptedException e)
         {
            // interupt is ok here.
         }

         // now hand the result from the call back. we simulate a fix result
         // We also could have caught the Exception and deal with it.
         result = "Simulated Result" ;
         // Since we are in the worker thread => inform EDT To close the dialog.
         SwingUtilities.invokeLater(()->modalDialog.setVisible(false));
      }

   }

}

最后是一些展示功能的代码

public class SampleUI extends JFrame
{
   JButton serverCallButton = new JButton("Call server") ;
   JLabel resultLabel = new JLabel("No result so far") ;
   public SampleUI()
   {
      JPanel cont = new JPanel() ;
      cont.add(serverCallButton) ;
      cont.add(resultLabel) ;
      this.setContentPane(cont);

      serverCallButton.addActionListener((e)->processServerButtonCall());

   }

   private void processServerButtonCall()
   {
      ServerRemoteInterface serverAccess = (ServerRemoteInterface) Proxy.newProxyInstance(SampleUI.class.getClassLoader(), new Class[]{ServerRemoteInterface.class}, new ServerProxy());

      String result = serverAccess.method1() ;
      resultLabel.setText(result);


   }

   public static void main(String[] args)
   {
      SampleUI sampleUI = new SampleUI() ;
      sampleUI.pack();
      sampleUI.setVisible(true);
   }


}

这个例子非常压缩,但应该展示原理。作为开发人员,我不必关心对服务器的调用实际上是服务器调用。对我来说,这就像调用本地电话一样。我什至不必关心我是否在 EDT 主题中,因为我就是这样。 正如我所说,在 FX 中,它的工作方式与模态阶段相同。我尝试将其设置为不透明 0.0 => 无论如何它都没有绘制。这有效。

问题仍然存在:有没有办法绕过额外的 JDialog 或 Stage ?

最佳答案

如果我正确理解你的意图,这是 Future 的一个用例:

CompletableFuture.supplyAsync(() -> myServerRemoteProxy.doThingsOnServer(...))
        .thenAccept(result -> doSomethingUseful(result));

服务器调用和 doSomethingUseful 都会在另一个线程中执行,因此如果需要,您需要在 doSomethingUseful 中使用 Platform.runLater访问场景图。

关于JavaFX 阻止 UI(和代码)直到服务器调用完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27015463/

相关文章:

java - 如何将自定义 Spring Data Neo4j 5.0.3 密码查询标记为只读

java - actionPerformed 无法看到 JButton 的引用变量

android - Android 中 View 导航的设计模式?

Qt Designer和Dock小部件

java - iText如何从可填充模板创建多页文档

java - 我的java二进制搜索代码有什么问题?

java - 如何在java程序中运行不同的java程序?

java - java中无法检测到图像

java - JEdi​​torPane 矩形(列)选择模式

java - 不同 ListView 项的不同选择颜色