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

时间:2014-11-19 11:14:28

标签: java swing user-interface javafx blocking

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

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

在Swing UI线程中(由侦听器)触发对服务器的调用。它会停止执行程序(监听器代码),尽管它是在一个额外的线程中完成的。 " doSomethingUsefull()"在服务器返回后调用。开发人员不必在此处理线程。 它是如何完成的?通过使用"旋转库" (http://spin.sourceforge.net/)。 它使用Swing EDT做了一些巧妙的技巧。 另一种方法是使用模态对话框,但我们决定不再有一个额外的窗口,而是使用glasspane禁用一些UI组件。

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

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

我们需要服务器远程接口。任何界面都可以。

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?

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

与Tomas一样,该解决方案是一个嵌套的事件循环。 Currectly Java FX已经有了这样的实现:

com.sun.javafx.tk.Toolkit.getToolkit()

提供方法enterNestedEventLoop和exitNestedEventLoop。

从包名称可以看出它是sun(oracle)特定的,不应该使用。我读到人们已经要求甲骨文公司将其移动到平台"因为它是一个非常有用的功能。

也许我们仍然使用它: - )