我在这里有这段代码,本质上,这是当用户按下“监听”按钮时运行的代码。
在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()){
}
}
答案 0 :(得分:2)
没有理由忙于等待Task
完成。 Task
提供了许多方法来注册到达特定State
的回调。这些机制之一是使用EventHandler
和各种属性(例如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 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()
方法内。