我目前正在创建一个使用Java FX创建的UI的Java应用程序。我的应用程序界面分为三个单独的选项卡。在第一个选项卡上,我有很多按钮可以启动其他应用程序。我的目标是让用户以特定顺序启动这些应用程序,因此在前一个应用程序的过程完成之前,后续按钮被禁用,然后启用它们。我已经通过使用行
完成了这项工作Process p = Runtime.getRuntime().exec(application);
p.waitFor();
然后控制器启用界面中的下一个按钮。启动的应用程序运行时出现问题。由于我的应用程序等待进程结束,任务管理器显示我的Java应用程序没有响应,并且您无法导航到其他选项卡。当然,当启动的应用程序完成后,我的应用程序正常运行。
我认为这是由于主要线程被阻止,因为它正在等待一个进程完成。最好的解决方案是在单独的线程上运行此进程,以便在等待已启动的进程完成时,应用程序的其余部分是否可用?
答案 0 :(得分:5)
我认为这是由于主要线程被阻止,因为它正在等待一个进程完成。
这是正确的。
是否可以在单独的线程上运行此进程,以便在等待已启动的进程完成时可以使用其余应用程序?
是的,当然。最简单的方法就是在一个单独的线程上启动它:
Process p = Runtime.getRuntime().exec(application);
new Thread(p::waitFor).start();
但是,你可能还需要更多:在新线程完成时收到通知等.JavaFX提供了concurrency API,它对您执行的任务的生命周期进行了回调。这些回调在FX应用程序线程上执行,因此更新回调中的UI是安全的(您无法从后台线程更新UI)。
所以你可以做点什么
Task<Void> executeAppTask = new Task<Void>() {
@Override
protected Void call() throws Exception {
Process p = Runtime.getRuntime().exec(application);
p.waitFor();
}
};
executeAppTask.setOnSucceeded(e -> {
/* code to execute when task completes normally */
});
executeAppTask.setOnFailed(e -> {
Throwable problem = executeAppTask.getException();
/* code to execute if task throws exception */
});
executeAppTask.setOnCancelled(e -> {
/* task was cancelled */
});
Thread thread = new Thread(executeAppTask);
thread.start();
请注意,setOnSucceeded()
,setOnFailed()
和setOnCancelled()
处理程序在FX应用程序线程上执行,因此,再次更新这些处理程序中的UI是安全的。另请注意,如果需要,调用executeAppTask.cancel()
将中断运行任务的线程,这将正确中断waitFor()
方法。因此,您将(或多或少)通过调用setOnCancelled
立即调用executeAppTask.cancel()
处理程序。如果在调用阻塞方法(例如call()
)后执行了<{1}}方法中的代码,它应该可以捕获中断的异常并检查{{1} }如果任务已被取消,则正常退出。
有关更多示例,请参阅Task
API docs。