GUI блокируется при использовании цикла while для проверки завершения задачи - PullRequest
0 голосов
/ 06 октября 2018

У меня есть этот фрагмент кода, по сути, это то, что он пытается сделать, когда пользователь нажимает кнопку «Слушать», когда этот код запускается.

В цикле for мы получаем одно имя (дляпример «Джон»), проиграть аудиозапись «Джон» с помощью команды bash;дождитесь окончания воспроизведения имени (цикл while), затем продолжайте получать следующее имя и воспроизводите его (и т. д.).Однако, хотя цикл while, по моему мнению, блокирует GUI в javafx, что является лучшим решением для этого?

Еще одна вещь, которую я хотел бы сделать - это когда поток запущен, я хочу проверить, не нажата ли кнопка «Прослушать» снова (если это так, я хочу остановить поток / задачу).

Как мне это сделать?

for (String s : _userDatabase.getSelectedNames()) {
                        Task<Integer> task = new Task<Integer>() {
                            try {
                                    Process pb = new ProcessBuilder("bash", "-c", //SomeCommand).start();
                                    return pb.waitFor();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                    return 1;
                                }
                        };

                        Thread playAudio = new Thread(task);
                        playAudio.setDaemon(true);
                        playAudio.start();
                        while(!task.isDone()){

                        }
                    }

1 Ответ

0 голосов
/ 06 октября 2018

Нет никаких причин, чтобы заняться ожиданием завершения Task.A Task предоставляет множество способов регистрации обратных вызовов, когда он достигает определенного значения State.Один из этих механизмов использует EventHandler s и различные свойства (например, onSucceeded, onFailed и т. Д.).Вы также можете прослушивать любое из соответствующих Worker свойств (например, state, value, exception и т. Д.).

Task<?> task = ...;
task.setOnSucceeded(event -> {
    // Do what needs to be done...
});
task.setOnFailed(event -> {
    // Do what needs to be done...
});
// execute task on background thread

Обработчики событий и / или прослушиватели свойстввсегда вызывается в потоке приложений JavaFX .

Поскольку вы создаете анонимный класс, вы также можете переопределить защищенные методы Task;эти методы также вызываются в потоке приложений JavaFX .

Task<Integer> task = new Task<>() {

    @Override
    protected Integer call() throws Exception {
        // background work...
    }

    @Override
    protected void succeeded() {
        // do what needs to be done
    }

    @Override
    protected void failed() {
        // do what needs to be done
    }

}

Примечание. Если вы переопределяете эти методы в ScheduledService, убедитесь, что при необходимости вызывается супер-реализация,См. Javadoc для получения дополнительной информации.


Есть несколько способов запустить следующий Task только после завершения предыдущего.Один из них - использовать один поток для запуска всех Task и поставить их в очередь.

ExecutorService executor = Executors.newSingleThreadExecutor();
for (String s : _userDatabase.getSelectedNames()) {
    Task<Integer> task = ...;
    // add callbacks to task if necessary
    executor.execute(task);
}
executor.shutdown();

Другой вариант - использовать Iterator.Один пример:

private void execute(final List<String> names, final Executor executor, 
                     final Consumer<Task<Integer>> onNewTask) {
  Objects.requireNonNull(names);
  Objects.requireNonNull(executor);
  Objects.requireNonNull(onNewTask);

  if (names.isEmpty()) {
    return;
  }

  final Iterator<String> iterator = names.iterator();

  class TaskImpl extends Task<Integer> {

    private final String name;

    private TaskImpl(String name) {
      this.name = name;
    }

    @Override
    protected Integer call() throws Exception {
      // background work...
    }

    @Override protected void succeeded() { complete(); }
    @Override protected void failed() { complete(); }
    // @Override protected void cancelled() { complete(); }

    private void complete() {
      if (iterator.hasNext()) {
        TaskImpl task = new TaskImpl(iterator.next());
        onNewTask.accept(task);
        executor.execute(task);
      }
    }

  }

  // launch first task
  TaskImpl firstTask = new TaskImpl(iterator.next());
  onNewTask.accept(firstTask);
  executor.execute(firstTask);
}

Третий вариант - сделать всю работу за один Task.По сути, поместите петлю for в метод call().

...