问题
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