{Java} Vaadin 14 - Определение ухода пользователя (закрытие вкладки, f5 и т. Д.)

В настоящее время я использую Vaadin Flow версии 14 (https://github.com/vaadin/platform/releases/tag/14.0.0).
Я использую версию 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");
        });

Я не могу использовать это, так как я использую Vaadin 14, а не 8.


Я попытался добавить это в свой класс просмотра:

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

Он печатает это сообщение только по истечении сеанса (в моем случае 15 секунд). Даже если я не закрою страницу.


Я попытался реализовать BeforeLeaveObserver / BeforeLeaveListener и переопределить это:

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

Это никогда не печатает.


Я тоже пробовал все это, но они не делают то, что мне нужно:

        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 распечатывается, когда пользователь загружает страницу.
1.1 addBeforeLeaveListener никогда не печатает.
2 addServiceDestroyListenerникогда не печатает. Сервис - это не то же самое, что сеанс. Это имеет смысл.
1.2 а также 3 addSessionDestroyListenerпечатает спустя некоторое время. Занимает примерно 15-30 секунд.


Нет ли способа мгновенно обнаружить это?:/

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, где прослушивание простоbeforeunload достаточно.

Это позволяет упростить JavaScript до

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

BeforeLeaveObserver работает с навигацией. Итак, если вы используете RouterLink или звонитеui.navigate("route")то передается событие BeforeLeaveEvent, но не при вызовеpage.setLocation().

Браузер может закрыться из-за сбоя или утерян по другим причинам, тогда beforeunloadестественно не бывает. Последнее средство - механизм обнаружения сердцебиения. Т.е. после трех ударов сердца сервер Vaadin придет к выводу, что Браузер утерян. В этом, естественно, есть отсрочка, которой просто не избежать.

В зависимости от того, какие браузеры вам нужно поддерживать, вы также можете взглянуть на Beacon API, который поддерживается всеми современными браузерами, но не IE11.

Преимущество Beacon API в том, что он неблокирующий. С помощью прослушивателя выгрузки клиент ожидает ответа перед закрытием вкладки, что может быть плохим для пользователя.

Тем не менее, как упомянул Тату, если браузер выйдет из строя или пользователь потеряет подключение к Интернету, все, что вы можете сделать, это дождаться истечения времени ожидания сеанса.

Решение для Vaadin Flow 22. Я поместил в index.html секцию сразу после закрывающего тега </style>:

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

Скорее всего, вы не увидите свое собственное сообщение в диалоговом окне — вы увидите сообщение по умолчанию. Я тоже не видела, но все равно применяю ;)

Другие вопросы по тегам