我正在尝试为网格提供自动刷新功能,该功能基本上是每隔n秒钟用服务器中的最新数据更新网格。
每当用户启用自动刷新时,我都可以实现PollListner。
UI ui= TestUI.getCurrent();
Boolean value = isRefreshChkBox.getValue();
PollListener listener = e -> {
explorer.reloadUI();
};
if (value) {
String refreshRateValue = refreshRateTxtField.getValue();
int refreshRate = Integer.valueOf(refreshRateValue);
int millis = (int) TimeUnit.SECONDS.toMillis(refreshRate);
absUI.setPollInterval(millis);
absUI.addPollListener(listener);
} else {
absUI.setPollInterval(-1);
absUI.removePollListener(listener);
}
使用上面的代码,每次启用自动刷新时我都会添加PollListener,并在禁用时将其删除。
我在这里找到了类似的问题我在这里VAADIN 7: What is the simplest way to refresh a Vaadin View in 5 minute intervals?
但是想知道是否有更好的方法来实现简单的用例AutoRefresh UI? PollListener应该在哪里实现?我想为视图创建一次PollListener,并且每次用户更改刷新率时都更新PollInterval。
关于哪种方法更好的指示,或者Vaadin中是否有全新的概念可以实现这一目标?
TIA
答案 0 :(得分:5)
请参见correct Answer by Leif Åstrand。我将添加一些讨论,并使用轮询和推送功能提供完整的示例应用程序。
Vaadin 8有两种自动更新信息显示而无需用户做出手势的方式:“轮询和推送”。
在Vaadin 8的Polling feature中,您的set a polling interval子类以UI
毫秒为单位。默认值-1
禁用轮询。
myUI.setPollInterval( ( int ) TimeUnit.MINUTES.toMillis( 1 ) ); // Specify milliseconds for polling interval.
启用后,安装在用户网络浏览器中的Vaadin JavaScript库将通过Vaadin服务器签入。作为PollNotifier
,UI签入会导致在服务器端触发事件。
如果您定义实现PollListener
接口的类,则实例将调用其poll
方法。
注册PollListener
后。返回一个Registration
对象。该对象提供了一种remove
方法来注销您的侦听器(如果需要)。
您可以选择使用lambda语法,匿名内部类或单独定义的类来定义PollListener
。
Registration registration = this.addPollListener( new UIEvents.PollListener() {
@Override
public void poll ( UIEvents.PollEvent pollEvent ) {
System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
…
}
} );
或者,lambda语法:
Registration registration = this.addPollListener( ( UIEvents.PollListener ) pollEvent -> {
System.out.println( "TRACE - PollListener::poll running. " + Instant.now() );
…
} );
在此调用期间,您的代码可以注册Runnable
,以便在方便时使用UI
子类进行调用。
Runnable
负责更新UI
子类中包含的窗口小部件。请记住 从不从后台线程访问或修改窗口小部件。您可能会摆脱它,或者可能导致可怕的事情发生。安全:请始终调用UI::access
来传递访问小部件的Runnable
。 Runnable
将在Web应用程序的主用户界面线程(负责UI
子类实例的线程)上运行。
getUI().access( new Runnable() {
@Override
public void run ( ) {
subscriber.refresh( new ArrayList <>( statusList ) ); // Copy the list in case the `Grid` modifies it, such as sorting.
}
} );
使用轮询功能的好处在于,您必须进行的编程比使用Push
更为简单(如下所述)。了解非用户自动生成的更新时,轮询可能是更好的选择。
一个简单的方面是,UI
子类的每个实例都负责自己的轮询,选择是否以及何时进行轮询以及控制轮询的频率。每个UI
子类实例都调用其自己的setPollInterval
方法。对用户来说,更多轮询可能是不错的选择,但是闲聊增加了网络流量,从而使您的网络管理员变得胡思乱想。因此,您可以通过UI
子类实例来调整频率。请记住,不仅每个用户都有自己的UI
子类实例,而且Vaadin 8还能够使用多窗口/选项卡应用程序。每个Web浏览器中的一个Web应用程序可以打开多个窗口/选项卡,每个窗口/选项卡运行各自的相同或不同UI
子类的实例。
从美学上讲,一个缺点是轮询破坏了HTTP设计的请求响应风格。尽管这是我的宠儿,但那艘船已经航行很久了,因此在这里我不会浪费太多时间来夸大使用文档传送系统作为交互式客户端-服务器应用程序体系结构的能力。
更实际的缺点是网络上不必要的流量。如果您能够通过Push或WebSocket使用Webpush,则客户端和服务器之间的开放连接将保持很少的通信量,直到服务器生成要通信的事件为止给客户。但是请注意,WebSocket很容易被防火墙和代理所击败,并且Webpush可能无法实现/不受支持,在这种情况下,Vaadin中的Push实现(Atmosphere Framework的async-io.org库)可能会退回到轮询技术。
另一个缺点是每个客户端执行自己的重复轮询效率低下,每个客户端在服务器端触发单独的执行,例如在数据库中搜索新数据。如果您有许多客户端都使用同一组不可变对象,则Push可以更有效地执行一次搜索新数据并将相同的数据对象交付给所有客户端的操作。
Vaadin与Atmosphere的组合(如上链接)大大简化了在Web应用中使用Push技术的过程。但是,与使用“轮询”功能所看到的相比,具有更多运动部件的情况要复杂一些。
首先,在UI
子类上启用带有@Push
批注的Push。
然后使用ScheduledExecutorService
安排每分钟触发一次事件。使用ServletContextListener
设置该执行程序。参见下面的示例代码。
如上所述,在能够使用WebSocket技术或Webpush的网络流量方面,推送可能非常有效。
不幸的是,WebSocket可能被防火墙和代理所击败。 Webpush是新的,可能不会得到广泛支持。在这种情况下,Vaadin / Atmosphere可能会退回到使用轮询方法。
另一个缺点是编码更加复杂。刚接触这项工作的程序员可能需要一段时间才能掌握各种动静的部分。
ScheduledExecutorService
处理线程和触发计划。 ServletContextListener
,如下所述。 请注意,某些推送方法(尤其是WebSocket)涉及维护开放的网络连接。因此,这会占用服务器计算机上的资源,例如端口号。
我使用Vaadin 8.6beta1构建了一个完整的工作示例应用程序。此应用程序支持两者轮询和推送。不知道您是否会在真正的Web应用程序中同时使用这两者,但也许可以。
访问主文件on my Google Drive。添加到通过Vaadin Ltd.提供的Maven原型vaadin-archetype-application
创建的项目中。
注意事项:此示例经过几天的兼职整理。因此,它可能是也可能不是生产就绪代码,并且可能会或可能不会显示适当的技术。但是希望它将有助于指导新手。
注意:我不是这个领域的专家。因此,将上面的所有讨论和示例代码都放在grain-of-salt中。做你自己的研究和学习。
此应用程序允许您通过单选按钮启用和禁用每种方法。您还可以通过单击立即手动刷新按钮来强制立即刷新。
绿色阴影表示自上次刷新以来更改的值。
您可以运行多个窗口。观看它们一起更新还是单独更新,或者不是全部更新,取决于您的单选按钮设置。
该示例应用程序的主要思想是模拟一个数据库,该数据库保持大约十种设备/过程/人/物的当前状态。每个状态由数字1-10标识。每个状态的状态都包含10个值(1-9)。每个状态都会记录上一次更新的时间。
这10条状态记录在Vaadin Grid 小部件中显示为行。
所有这些数据都记录在关系数据库H2 Database Engine中。作为演示,我们不需要持久性,因此数据库是内存中的。后台线程随机更新数据库中的状态行。
MyDbService.java
此数据库服务代码建立我们的内存H2数据库,为我们的Status定义表,并填充十行。该类还可以随机更新某些行的值。并且您可以要求检索表示当前存储值的List
个对象中的Status
个。
Status.java
每个状态记录都由Java Status
类(一个简单的POJO)表示。
Vaadin基于Java Servlet技术。 Vaadin应用程序是Servlet的一个重要实现。作为Servlet,它可以响应用户网络浏览器的传入请求。
在第一个传入请求之前,我们需要进行一些设置工作。一方面,我们需要使用十个状态记录来建立并填充该数据库。
Servlet规范要求所有web containers都支持ServletContextListener
接口。如果编写实现该接口的类并将其声明到Web容器,则将在第一个请求之前和最后一个请求之后调用它。
在我们的示例中,我们使用该挂钩建立数据库。我们还设置了一个后台线程,该线程随机更改我们存储的状态记录,以模拟用户的更新或Feed中的最新数据。
这是我们的示例ServletContextListener
。
最简单的方法是在@WebListener
批注中声明其在我们的Web容器中的存在,但是您可以在部署方案中根据需要选择其他路由。
@WebListener
public class MyServletContextListener implements ServletContextListener {
…
MyUI.java
此Vaadin Web应用程序的入口点是我们的UI
,MyUI.java
子类。它有两个工作:(a)在屏幕上获取我们的用户界面内容,以及(b)将自己注册为PollListener
以对轮询更新做出反应。
DataDisplayLayout.java
这是我们的用户界面内容。这是此示例应用程序的核心。它显示Vaadin网格,其显示将用新数据更新。
DataDisplayLayoutRefreshManager.java
此经理负责管理要注册DataDisplayLayout
实例(希望通过Push更新)的pub-sub(发布-订阅)模型。
此处使用弱引用的集合来跟踪订户。因此,订阅的DataDisplayLayout
实例可以优雅地通知其不再更新的愿望,或者实例可以简单地超出范围,最终成为订户。
轮询方法不需要此管理器,因为我们的UI
子类(MyUI
)的每个实例都在单独轮询服务器。
mytheme.scss
Vaadin网格中表示新值的单元格的绿色是通过CSS设置的。在Vaadin 8中,我们通过编辑埋藏在项目mytheme.scss
文件夹中的webapp
文件来实现此目的。
在这里我们定义样式名称fresh_row
。
@import "../valo/valo.scss";
@mixin mytheme {
@include valo;
// Insert your own theme rules here
.v-grid-row.fresh_row > td:nth-child(2) {
background-color: honeydew;
}
}
我们必须通过实现样式生成器将该样式名称分配给Vaadin Grid行。
this.grid.setStyleGenerator( ( StyleGenerator ) o -> {
Status s = ( Status ) o;
if ( s.getUpdated().isAfter( this.whenRowLastUpdated ) ) {
return "fresh_row";
} else {
return null;
}
} );
答案 1 :(得分:1)
基本上有两种从后台活动更新Vaadin UI的方法:轮询和推送。每个人都有自己的优点和缺点。
轮询是技术上更简单的方法。它基于浏览器中的计时器,该计时器会定期触发请求。任何未决的更改将在对该请求的响应中交付给客户端。此外,您可以添加针对每个此类请求运行的侦听器,以便您可以手动检查更改,并在需要时更新UI。
推送基于保持客户端与服务器之间持久连接的打开,以便服务器可以立即将更改发送给客户端,而不必等到客户端打开连接并请求更改。这样做的好处是,更改可以在发生时立即发送给客户端,而不是定期发送。
哪种使用取决于您的要求。轮询可以使用较少的资源,因为不需要一直保持连接打开。如果在数据更改时没有服务器端触发,则轮询也可能是有益的,但是,服务器端逻辑仍然必须定期显式检查是否有任何更改。推送的主要好处是,更改可以在发生问题时立即发送。