使用while循环检查任务何时完成时,GUI被阻止

时间:2018-10-06 20:06:29

标签: java javafx task

我在这里有这段代码,本质上,这是当用户按下“监听”按钮时运行的代码。

在for循环中,我们得到一个名字(例如“ John”),通过bash命令播放“ John”的录音;等到名称播放完毕(while循环),然后继续获取下一个名称并播放(以此类推)。但是我认为while循环阻塞了javafx中的GUI,对此有什么更好的解决方案?

我想做的另一件事是当线程启动时,我想检查是否再次按下了“侦听”按钮(如果是,我想停止线程/任务)。

我该怎么做?

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 :(得分:2)

没有理由忙于等待Task完成。 Task提供了许多方法来注册到达特定State的回调。这些机制之一是使用EventHandler和各种属性(例如onSucceededonFailed等)。您还可以收听任何适当的Worker属性(例如statevalueexception等)。

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 Application线程上被调用。

由于您正在创建匿名类,因此您还可以覆盖Task的受保护方法;这些方法在 JavaFX Application Thread 上也被调用。

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()方法内。