See the Leif Åstrand 的正确答案。我将添加一些讨论,以及使用轮询和推送的完整示例应用程序。
Vaadin 8 有两种无需用户做出手势即可自动更新信息显示的方式:轮询和推送。
Polling
在 Vaadin 8 中投票功能, you 设置轮询间隔您的毫秒数UI子类。默认值为-1
禁用轮询。
myUI.setPollInterval( ( int ) TimeUnit.MINUTES.toMillis( 1 ) ); // Specify milliseconds for polling interval.
启用后,安装在用户 Web 浏览器中的 Vaadin JavaScript 库会与 Vaadin 服务器签入。成为一个PollNotifier,UI 签入会导致服务器端触发一个事件。
如果您定义一个实现了PollListener接口,您的实例将有它的poll调用的方法。
注册您的PollListener
。找回一个Registration
目的。该对象提供了一个remove
如果需要的话,取消注册您的侦听器的方法。
您可以选择定义您的PollListener
使用 lambda 语法、匿名内部类或单独定义的类。
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
子类。
That Runnable
更新您的中包含的小部件的工作UI
子类。记得never从后台线程访问或修改小部件。你可能会侥幸逃脱,也可能会导致可怕的事情发生。确保安全:始终致电UI::access
通过一个Runnable
访问小部件。那Runnable
将在您的网络应用程序的主用户界面线程上运行,该线程负责您的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.
}
} );
Pros
使用轮询功能的好处是您必须执行的编程比使用轮询功能更简单Push
(下面讨论)。在了解自动非用户生成的更新时,轮询可能是更好的途径。
一个简单的方面是您的每个实例UI
子类负责自己的轮询,选择是否以及何时进行轮询以及控制轮询的频率。每个UI
子类实例调用自己的实例setPollInterval方法。更多的轮询可能对用户来说很好,但闲聊会增加网络流量,从而使您的网络管理员变得暴躁。所以你可以通过以下方式调整频率UI
子类实例。请记住,每个用户不仅拥有自己的UI
子类实例,而且 Vaadin 8 能够运行多窗口/选项卡应用程序。每个网络浏览器中的一个网络应用程序可以打开多个窗口/选项卡,每个窗口/选项卡都运行自己的相同或不同的实例UI
子类。
Cons
美学上的一个缺点是轮询破坏了 HTTP 设计的请求-响应优雅性。虽然这是我最讨厌的事情,但那艘船很久以前就已经航行了,所以我不会在这里浪费字节来抱怨使用文档传递系统作为交互式客户端-服务器应用程序架构。
更实际的缺点是网络上不必要的流量。如果您能够使用Push via WebSocket or Webpush,然后客户端和服务器之间一直保持一个开放的连接,流量非常小,直到服务器生成要传达给客户端的事件。但请注意,WebSocket 很容易被防火墙和代理击败,并且 Webpush 可能无法实现/支持,在这种情况下,Vaadin 中的 Push 实现(氛围框架图书馆由async-io.org)可能会退回到轮询技术。
另一个缺点是每个客户端都进行自己的重复轮询,并且每个客户端都触发服务器端的单独执行,例如在数据库中搜索新数据,效率低下。如果您有许多客户端都使用同一组不可变对象,则推送可以更有效地执行对新数据的单次搜索并将同一组数据对象传递给所有客户端。
Push
Vaadin 与 Atmosphere 的结合(上面链接)vastly简化在您的网络应用程序中使用推送技术。然而,与轮询功能相比,它有更多的移动部件,因此稍微复杂一些。
首先,启用推送@Push注释在你的UI
子类。
然后使用以下方法安排每分钟触发一个事件ScheduledExecutorService
。设置该执行器ServletContextListener
。有关这一切,请参阅下面的示例代码。
Pros
就可使用的网络流量而言,推送可以非常高效WebSocket技术或Webpush, 正如刚才提到的。
Cons
不幸的是,WebSocket 可能会被防火墙和代理击败。而且 Webpush 是新事物,可能不会得到广泛支持。在这种情况下,Vaadin/Atmosphere 可能会转而使用轮询方法。
另一个缺点是编码有点复杂。刚接触这项工作的程序员可能需要一段时间才能掌握各种移动部分。
- 您需要服务器端的后台线程来跟踪时间,在我们的例子中每分钟触发一次。现代方法是使用
ScheduledExecutorService
处理线程和触发时间表。
- 要设置该执行程序服务,您需要实现
ServletContextListener
如下所述。
请注意,某些推送方法(尤其是 WebSocket)涉及维护开放的网络连接。因此,这会消耗服务器计算机上的资源,例如端口号。
示例应用程序
我使用 Vaadin 8.6beta1 构建了一个完整的工作示例应用程序。这个应用程序支持both轮询和推送。不确定您是否会在真正的网络应用程序中混合使用两者,但也许吧。
访问主要文件在我的 Google 云端硬盘上。添加到通过 Maven 原型创建的项目vaadin-archetype-application
由 Vaadin 有限公司提供
警告:这个例子是花了几天时间兼职拼凑起来的。因此,它可能是也可能不是生产就绪的代码,并且可能会也可能不会显示正确的技术。但希望它能帮助指导新手。
警告:我不是这个领域的专家。因此,将我上面所有的讨论和我的示例代码与盐粒。做你自己的研究和学习。
该应用程序允许您通过单选按钮启用和禁用每种方法。您还可以通过单击强制立即刷新现在手动刷新 button.
绿色阴影表示自上次刷新以来更改的值。
您可以运行多个窗口。观看它们一起更新、单独更新或不全部更新,具体取决于您的单选按钮设置。
Database
此示例应用程序的主要思想是模拟一个数据库,维护大约十件设备/流程/人员/其他内容的当前状态。每个状态都由数字 1-10 标识。每个状态都有一个包含 10 个值(1-9)的域的状态。每个状态都会记录最后一次更新的时刻。
这十个状态记录在 Vaadin 中显示为行Grid widget.
所有这些数据都记录在关系数据库中,H2数据库引擎。作为演示,我们不需要持久性,因此数据库位于内存中。后台线程随机更新数据库中的状态行。
MyDbService.java
此数据库服务代码建立我们的内存 H2 数据库,定义状态表并填充十行。该类还可以随机更新某些行的值。您可以要求检索List
of Status
表示当前存储值的对象。
Status.java
每个状态记录在 Java 中表示为Status
类,一个简单的 POJO。
生命周期
瓦丁基于Java小服务程序技术。您的 Vaadin 应用程序是一个大型 Servlet 实现。作为 servlet,它响应用户 Web 浏览器传入的请求。
在第一个传入请求之前,我们需要做一些设置工作。一方面,我们需要建立数据库并使用十个状态记录填充该数据库。
Servlet规范要求所有网络容器支持ServletContextListener
界面。如果您编写一个实现该接口的类,并将其声明给 Web 容器,那么它将在第一个请求之前和最后一个请求之后调用。
在我们的示例中,我们使用该挂钩来建立数据库。我们还设置了一个后台线程,可以随机更改存储的状态记录,以模拟用户的更新或来自提要的新数据。
上下文监听器
这是我们的例子ServletContextListener
.
向我们的 Web 容器声明其存在的最简单方法是通过@WebListener注解,但您可以根据部署场景的需要选择其他路由。
@WebListener
public class MyServletContextListener implements ServletContextListener {
…
MyUI.java
这个 Vaadin Web 应用程序的入口点是我们的子类UI
, MyUI.java
。它有两项工作:(a) 将我们的用户界面内容显示在屏幕上,以及 (b) 将自己注册为PollListener
对轮询更新做出反应。
DataDisplayLayout.java
这是我们的用户界面内容。这是此示例应用程序的核心。它显示 Vaadin 网格,其显示将使用新数据进行更新。
DataDisplayLayoutRefreshManager.java
该经理负责监督注册我们的实例的发布-订阅(发布-订阅)模型DataDisplayLayout
想要通过推送更新。
这里使用弱引用的集合来跟踪订阅者。所以订阅的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 网格行。
this.grid.setStyleGenerator( ( StyleGenerator ) o -> {
Status s = ( Status ) o;
if ( s.getUpdated().isAfter( this.whenRowLastUpdated ) ) {
return "fresh_row";
} else {
return null;
}
} );