更新:
我在 JavaFx 应用程序上有一个按钮,应该在用户输入电子邮件和密码后进行登录。
<Button fx:id="loginButton" layoutX="157.0" layoutY="254.0" mnemonicParsing="false" onAction="#login" prefHeight="30.0" prefWidth="172.0" text="Login" />
我在 JavaFX 应用程序上有一个 ExecutorService。我创建了一个搜索数据库的任务。它很好地执行了查询并获取了 UserInfo 对象,问题是 ExecutorService 没有将结果传递给主线程。这是执行程序服务返回 null 问题的主要代码:
public class LoginController {
@FXML
private Button loginButton;
@FXML
private Label afterLoginText;
@FXML
private TextField email;
@FXML
private PasswordField password;
@FXML
private Hyperlink hyperlink;
@FXML
private ProgressBar progressBar;
private Navegador navegador;
public void login(ActionEvent event) {
afterLoginText.setText("Login in, please wait...");
String emailText = email.getText();
String passwordText = password.getText();
DAOGeneric<UserInfo> dao = new DAOGeneric<>();
LoginAtDataBaseTask loginAtDataBaseTask = new LoginAtDataBaseTask(dao, emailText, passwordText);
progressBar.progressProperty().bind(loginAtDataBaseTask.progressProperty());
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(loginAtDataBaseTask);
loginAtDataBaseTask.setOnSucceeded(workerStateEvent -> {
UserInfo userInfo;
try {
userInfo = (UserInfo) future.get();
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(userInfo == null){
System.out.println("User info is null");
}
else{
try {
changeToMainScreen(event, userInfo);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
//Set premium level if user have one
//Optional - show premium info
}
});
executorService.shutdown();
}
public void changeToMainScreen(ActionEvent event, UserInfo userInfo) throws IOException {
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
runMainScreen("/mainScreen.fxml",stage, userInfo);
}
这是 LoginAtDataBaseTask 类的代码:
public class LoginAtDataBaseTask extends Task <UserInfo> {
private static DAOGeneric<UserInfo> dao;
private static String email;
private static String password;
public LoginAtDataBaseTask(DAOGeneric<UserInfo> dao, String email, String password) {
this.dao = dao;
this.email = email;
this.password = password;
}
@Override
protected UserInfo call() {
return doLogin();
}
private UserInfo doLogin(){
final int maxProgress = 100;
List<UserInfo> usersList = dao.findByAnyParameter("email", email, UserInfo.class);
if(usersList.size() == 1){
updateProgress(99,maxProgress);
UserInfo user1 = usersList.get(0);
String passwordDecoded = DecoderMD5.StringToMD5(password);
if(user1.getUserPassword().equals(passwordDecoded)){
// afterLoginText.setText("Login Sucess!");
return user1;
}
else{
// afterLoginText.setText("Wrong Password!");
}
}
else if(usersList.size()>1){
//More than one user with same email on BD
// afterLoginText.setText("Error code 1 - report to administrator");
}
else if(usersList.size()==0){
// afterLoginText.setText("This email is not registered! Please register first!");
}
else{
//Erro at DAO Search
// afterLoginText.setText("Error code 2 - report to administrator");
}
return null;
}
}
我尝试了多种方式进行转换,并首先使用 Future 来接收提交,然后在 future 对象上调用 get ,但似乎没有任何效果。我已经阅读了相关类的java文档,但我不太明白为什么我的对象保持为空。
更新:我设置了 setOnSucceeded 但 future.get 一直返回 null,并且主 javafx ui 一直卡住。我一直做错了什么以及我能做些什么来解决它?
最佳答案
为什么get()
返回null
?
这与 Task
有关。从根本上来说,是 Runnable
的实现。它不是 Callable
的实现。因此,您调用 #submit(Runnable)
它返回 Future<?>
1,表示预计不会有结果。一个Runnable
无法返回值。换句话说,调用#get()
将始终返回 null
在这种情况下。
但你真的应该打电话 #execute(Runnable)
当经过Task
时到ExecutorService
, 反正。没有理由拥有 Future
代表 Task
状态的对象。这至少有两个原因:
调用
Future#get()
是一个阻塞调用。Task
的全部目的是将结果专门传送回JavaFX应用程序线程。并且您绝不能永远阻止该线程,因为这样做会导致 UI 无响应。A
Task
是FutureTask
2,这意味着它已经是Future
。如果您确实需要等待结果(不在 FX 线程上),那么您可以调用#get()
关于Task
实例。
1. 它确实应该被定义为返回 Future<Void>
2. 请注意,标准 ExecutorService
实现,ThreadPoolExecutor
,包含所有提交的 Runnable
和Callable
FutureTask
中的对象,至少默认情况下是这样。
如何获取 Task
的结果
如前所述,Task
的目的(以及其他 javafx.concurrent
类)的作用是将工作卸载到后台线程,但将结果(以及消息、进度等)传回JavaFX 应用程序线程。特别是该线程,而不是任何其他线程。但您不能阻塞 FX 线程。这意味着观察任务是否完成,而不是等待任务完成。然后当它完成时,你通过做需要做的事情来使用react。
但是如何从已完成的 Task
中获取值?你查询其 value
属性,该属性将设置为 #call()
返回的任何内容。任务成功时的方法。如果需要,您可以使用监听器直接观察此属性。就我个人而言,我更喜欢使用 onSucceeded
和onFailed
特性。例如:
Task<SomeObject> task = ...;
task.setOnSucceeded(e -> {
SomeObject result = task.getValue();
// do something with 'result'
});
task.setOnFailed(e -> {
task.getException().printStackTrace(); // or log it with a proper logging framework
// notify user of failure
});
executor.execute(task);
注意 Task
的属性喜欢 message
, progress
, value
等等保证仅由 FX 线程设置。还有onXXX
处理程序也保证仅由 FX 线程调用。
参见Concurrency in JavaFX和 javafx.concurrent
文档以获取更多信息。
关于javafx - 为什么 ExecutorService 不返回我的任务结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75104843/