{Java} Vaadin 14 - Detect user leave (closes tab, f5, etc)

烈酒焚心 提交于 2020-02-24 12:14:07

问题


I'm currently using Vaadin Flow version 14 (https://github.com/vaadin/platform/releases/tag/14.0.0)
I run Java version 1.8.0_231, 64 bit.

I simply want to be able to detect (in java!) whenever a user does either of these actions:

  • Closes the current browser tab (or their browser)
  • Clicks F5 on their keyboard, or pressed the Refresh button in their browser
  • Clicks a link (or anything else), which redirects them away from my website

I've tried lots of different things to detect this. The only thing I have been able to detect so far, is whenever the current VaadinSession expires (which I can change by doing VaadinSession.getCurrent().getSession().setMaxInactiveInterval(15)). This makes every session expire after 15 seconds (15 is just a testing number in my case).



With Vaadin 8, I believe you can just do this and it will work out of the box:

        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");
        });

I can't use this tho, since I use Vaadin 14, not 8.


I have tried adding this to my View class:

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

It only prints this message when the session expires (15 seconds in my case). Even if I don't close the page.


I have tried implementing BeforeLeaveObserver / BeforeLeaveListener and overridding this:

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

This never prints.


I've also tried all of these, but they do not do what I need:

        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);
        });

1 addUIInitListener gets printed when the user loads the page.
1.1 addBeforeLeaveListener never prints.
2 addServiceDestroyListener never prints. A Service isn't the same as a Session. Makes sense tho.
1.2 and 3 addSessionDestroyListener prints wafter awhile. Takes like 15-30 seconds tho.


Is there not a way to detect this basically instantly? :/


回答1:


The JavaScript approach to detect closing still works.

The syntax just has changed a little

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");
}

Although this is not 100% bullet proof way, since I think not the all browsers work the same way. For example the above code triggers twice with Chrome, where listening just beforeunload is enough.

This allows to simplify the JavaScript to

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

BeforeLeaveObserver works with navigation. So if you use RouterLink or call ui.navigate("route") then BeforeLeaveEvent is dispatched, but not when you call page.setLocation().

Browser can close due crash, or lost due other reasons, then beforeunload naturally does not happen. The last resort is the heart beat based detection mechanism. I.e. after three lost heart beats Vaadin server will conclude that Browser was lost. There is naturally a delay in this, which simply cannot be avoided.




回答2:


Depending on which browsers you need to support, you might also want to take a look at the Beacon API, which is supported by all modern browsers, but not IE11.

The Beacon API has the benefit of being non-blocking. With the unload listener, the client waits for a response before closing the tab, which might be bad user experience.

Still, as Tatu mentioned, if the browser crashes or the user loses internet connection, all you can do is wait for the session to time out.



来源:https://stackoverflow.com/questions/60084612/java-vaadin-14-detect-user-leave-closes-tab-f5-etc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!