提问者:小点点

{Java} Vaadin 14 -检测用户离开(关闭选项卡,f5等)


我目前使用的是Vaadin Flow版本14(https://github . com/vaa din/platform/releases/tag/14 . 0 . 0)< br >我运行的是Java版本1.8.0_231,64位。

我只想能够检测(在java中)用户执行以下任一操作:

  • 关闭当前浏览器选项卡(或其浏览器)
  • 单击键盘上的F5,或按浏览器中的“刷新”按钮
  • 单击链接(或其他任何链接),将其从我的网站重定向

我已经尝试了很多不同的方式来检测这一点。到目前为止,我唯一能够检测到的是当前VaadinSession何时到期(我可以通过执行VaadinSession.getCurrent().getSession().setMaxInactiveInterval(15)来更改)。这使得每个会话在 15 秒后过期(在我的情况下,15 只是一个测试数字)。



使用 Vaadin 8,我相信你可以做到这一点,它会开箱即用:

        JavaScript.getCurrent().execute(
            "function closeListener() { catchClose(); } " +
                    "window.addEventListener('beforeunload', closeListener); " +
                    "window.addEventListener('unload', closeListener);"
        );
        JavaScript.getCurrent().addFunction("catchClose", arguments ->
        {
            System.out.println("user has quit :o");
        });

我不能用这个tho,因为我用的是Vaadin 14,不是8。< br>

我尝试将它添加到我的视图类中:

    @Override
    protected void onDetach(DetachEvent detachEvent)
    {
        System.out.println("onDetach " + detachEvent);
    }

它仅在会话到期时(在我的情况下为15秒)打印此消息。即使我不关闭页面


我尝试过实现BeforeLeaveObserver/BeforeLeave Listener,并将其改写为:

    @Override
    public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent)
    {
        System.out.println("beforeLeave");
    }

这个永远不会打印出来。< br>

我也尝试了所有这些方法,但它们都不能满足我的需求:

        VaadinResponse.getCurrent().getService().addUIInitListener(e ->
        {
            System.out.println("1 addUIInitListener : " + e);
            e.getUI().addBeforeLeaveListener(e2 ->
            {
                System.out.println("1.1 addBeforeLeaveListener : " + e2);
            });
            e.getUI().addDetachListener(e2 ->
            {
                System.out.println("1.2 addDetachListener : " + e2);
            });
        });
        VaadinResponse.getCurrent().getService().addServiceDestroyListener(e ->
        {
            System.out.println("2 addServiceDestroyListener : " + e);
        });
        VaadinResponse.getCurrent().getService().addSessionDestroyListener(e ->
        {
            System.out.println("3 addSessionDestroyListener : " + e);
        });

当用户加载页面时,打印< code>1 addUIInitListener。< br > < code > 1.1 addbeforelievelistener 从不打印。< br > < code > 2 addservicedestroy listener 从不打印。服务不同于会话。有道理。< br> 1.2和< code > 3 addSessionDestroyListener 稍后打印。大概需要15-30秒。< br>

有没有一种方法可以基本上立即检测到这一点?:/


共3个答案

匿名用户

检测关闭的JavaScript方法仍然有效。

语法只是改变了一点点

UI.getCurrent().getPage().executeJs("function closeListener() { $0.$server.windowClosed(); } " +
        "window.addEventListener('beforeunload', closeListener); " +
        "window.addEventListener('unload', closeListener);",getElement());


@ClientCallable
public void windowClosed() {
    System.out.println("Window closed");
}

虽然这不是 100% 防弹的方式,因为我认为并非所有浏览器的工作方式都相同。例如,上面的代码在 Chrome 中触发了两次,在卸载之前侦听就足够了。

这允许将JavaScript简化为

    UI.getCurrent().getPage().executeJs(
"window.addEventListener('beforeunload', () => $0.$server.windowClosed()); ",getElement());

BeforeLeaveObserver使用导航。因此,如果您使用RouterLink或调用ui.navigate(“route”),则BeforeLeaveEvent将被调度,但当您调用page.setLocation()时则不会被调度。

浏览器可能由于崩溃而关闭,或者由于其他原因而丢失,那么在卸载之前自然不会发生。最后的手段是基于心跳的检测机制。即在三次失去心跳后,Vaadin 服务器将得出结论,浏览器丢失了。这自然有延迟,这是根本无法避免的。

匿名用户

根据您需要支持的浏览器,您可能还需要查看所有现代浏览器都支持的 Beacon API,但不支持 IE11。

Beacon API具有非阻塞的优点。使用卸载侦听器,客户端在关闭选项卡之前等待响应,这可能是糟糕的用户体验。

然而,正如塔图提到的,如果浏览器崩溃或用户失去互联网连接,你所能做的就是等待会话超时。

匿名用户

Vaadin Flow 22的解决方案。我在index.html中放置了一节

  <script>
      window.addEventListener('beforeunload', function (event) {
       event.returnValue = 'Are you sure you want to close the application?';
      });
  </script>

最有可能的是,您不会在对话框窗口中看到自定义消息 - 您将看到默认消息。我也没有看到它,但仍然应用它;)