4.5.6.1. Использование фоновых задач

  1. Задача описывается как наследник абстрактного класса BackgroundTask. В конструктор задачи необходимо передать ссылку на контроллер экрана, с которым будет связана задача, и значение таймаута ее выполнения.

    Если экран указан, то при его закрытии пользователем активная задача будет прервана. Кроме того, задача будет автоматически прервана по истечении указанного таймаута.

    Собственно действия, выполняемые задачей, реализуются в методе run().

  2. Создается объект управления задачей − BackgroundTaskHandler. Для этого экземпляр задачи необходимо передать методу handle() бина BackgroundWorker. Ссылку на BackgroundWorker можно получить инжекцией в контроллер экрана, либо статическим методом класса AppBeans.

  3. Выполняется запуск задачи.

Пример:
@Inject
protected BackgroundWorker backgroundWorker;

@Override
public void init(Map<String, Object> params) {
  // Create task with 10 sec timeout and this screen as owner
  BackgroundTask<Integer, Void> task = new BackgroundTask<Integer, Void>(10, this) {
      @Override
      public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
          // Do something in background thread
          for (int i = 0; i < 5; i++) {
              TimeUnit.SECONDS.sleep(1); // time consuming computations
              taskLifeCycle.publish(i); // publish current progress to show it in progress() method
          }
          return null;
      }

      @Override
      public void canceled() {
          // Do something in UI thread if the task is canceled
      }

      @Override
      public void done(Void result) {
          // Do something in UI thread when the task is done
      }

      @Override
      public void progress(List<Integer> changes) {
          // Show current progress in UI thread
      }
  };
  // Get task handler object and run the task
  BackgroundTaskHandler taskHandler = backgroundWorker.handle(task);
  taskHandler.execute();
}

Подробная информация о назначении методов приведена в JavaDocs классов BackgroundTask, TaskLifeCycle, BackgroundTaskHandler.

Ниже приведены моменты, на которые следует обратить внимание:

  • BackgroundTask<T, V> − параметризованный класс:

    • T − тип объектов, показывающих прогресс задачи. Объекты этого типа передаются в метод progress() задачи при вызове TaskLifeCycle.publish() в рабочем потоке.

    • V − тип результата задачи, он передается в метод done(). Его также можно получить вызовом метода BackgroundTaskHandler.getResult(), что приведет к ожиданию завершения задачи.

  • Метод canceled() вызывается только в случае управляемой отмены задачи, то есть при вызове cancel() у TaskHandler.

  • Если у задачи истек таймаут, или было закрыто окно, в котором она исполнялась, то задача будет завершена без уведомлений. В блоке Web Client завершение по таймауту производится с задержкой, задаваемой свойством приложения cuba.backgroundWorker.maxClientLatencySeconds .

  • Метод run() задачи должен поддерживать возможность прерывания извне. Для этого в долгих процессах желательно периодически проверять флаг TaskLifeCycle.isInterrupted(), и соответственно завершать выполнение. Кроме того, нельзя тихо проглатывать исключение InterruptedException (или вообще все исключения). Вместо этого нужно либо вообще не перехватывать его, либо выполнять корректный выход из метода.

  • Объекты BackgroundTask не имеют состояния. Если при реализации конкретного класса задачи не заводить полей для хранения промежуточных данных, то можно запускать несколько параллельно работающих процессов, используя единственный экземпляр задачи.

  • Объект BackgroundHandler можно запускать (т.е. вызывать его метод execute()) всего один раз. Если требуется частый перезапуск задачи, то используйте класс BackgroundTaskWrapper.

  • Для показа пользователю модального окна с прогрессом и кнопкой Отмена используйте классы BackgroundWorkWindow или BackgroundWorkProgressWindow с набором статических методов. Для окна можно задать режим отображения прогресса и разрешить или запретить отмену фоновой задачи.

  • Если внутри потока задачи необходимо использовать некоторые значения визуальных компонентов, то нужно реализовать их получение в методе getParams(), который выполняется в потоке UI один раз при запуске задачи. В методе run() эти параметры будут доступны через метод getParams() объекта TaskLifeCycle.

  • При возникновении исключительных ситуаций в потоке UI вызывается метод BackgroundTask.handleException(), в котором можно отобразить ошибку.