JavaFX阻止UI(和代码),直到服务器调用完成 [英] JavaFX Block the UI (and the code) until a server call is finished

查看:175
本文介绍了JavaFX阻止UI(和代码),直到服务器调用完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This topic is around in countless articles of "not blocking the JavaFX UI during a long lasting call into something time consuming (server)". I know that and I googled and tried a lot. Most of the "Internet" explains that long lasting calls caused by JavaFX events need to be made in an extra thread. After the thread is finished, you update the FX UI with the results via Platform.runLater(). Most of the FX Libraries encapsulate this with sophisticated code constructs (really cool stuff). My current problem with it is: We are migrating a Swing rich client to JavaFX. It is a huge one, so we have to constantly include/replace JavaFX UI into it, until it is a full FX client. There is some functionality in the client that does a server call and has to wait before the user can continue with his work. The server uses JEE6 with stateless session beans and interfaces. The interfaces are known to the client and with a little library of our own, we implemented a little proxy hiding away the communication layer from the client. The client just creates a "RemoteProxy" with the library then just calling the remote interface and the library propagates the call to the server. The method is called and the result or Exception transported back to the client. For the client this appears like a local call. Here is the problem. A typical code fragment looks like this:

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

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

The call to the server is triggered in the Swing UI thread (by a listener). It stops the execution of the program (Listener Code) although it is done in an extra thread. "doSomethingUsefull()" is called after the server got back. The developer does not have to take care about threading here. How is it accomplished? By using the "Spin Library" (http://spin.sourceforge.net/). It does some clever tricks with the Swing EDT. An alternative would be to use a modal Dialog, but we decided not not have an extra window, but have a glasspane disabling some UI components instead.

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

So long explanation and short question... Is there something similar for JavaFX helping us to seamlessly call a server, stop the program execution until it got back and NOT blocking the JavaFX UI? Best would be if it can work together with Java Swing parts of code.

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

EDIT... Adding an very compressed example for demonstration of the use with hidden JDialog.

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

We need the server remote interface. Any interface will do.

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

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

Then we need the Proxy Invocation Handler

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 =>无论如何它都没有绘制。这是有效的。

The example is very compressed but should show the principle. As a developer I do not have to take care that the call to the server is really a server call. To me it's like a local call. I do not even have to take care that I am in the EDT Thread, because i just am. As I said it would work the same way in FX with a modal stage. I tried to set it to opaque 0.0 => It is not drawn anyway. This works.

问题仍然存在:是否有办法绕过额外的JDialog或舞台?

The question remains: Are there ways to get around the extra JDialog or Stage ?

推荐答案

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

Like Tomas said the solution is a nested Event Loop. Currectly Java FX already has such an implementation:

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

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

提供方法enterNestedEventLoop和exitNestedEventLoop。

provides the methods enterNestedEventLoop and exitNestedEventLoop.

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

From the package name you can tell that it is sun(oracle) specific and should not be used. I read that people already asked Oracle to move it it "Platform" because it is a very useful feature.

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

Maybe we use it anyway :-)

这篇关于JavaFX阻止UI(和代码),直到服务器调用完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆