UPDATE:
I have a button on a JavaFx App that should do the login after user input email and password.
<Button fx:id="loginButton" layoutX="157.0" layoutY="254.0" mnemonicParsing="false" onAction="#login" prefHeight="30.0" prefWidth="172.0" text="Login" />
I have an ExecutorService on a JavaFX app. I created a task to search the DB. It executes the query well and gets the UserInfo object, the problem is, the ExecutorService is not passing the results to the main thread. This is the main code that is having a problem of null returned from the executor service:
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);
}
And here is the code of the LoginAtDataBaseTask Class:
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;
}
}
I tried casting on many ways and using Future first to receive the submit and then calling get on the future object, but nothing seems to work. I have already readed the java documents of this related classes but i don't really understand why my object keeps null.
UPDATE: I put a setOnSucceeded but the future.get keeps returning null, and the main javafx ui keeps freezing. What am i keeping doing wrong and what can i do to solve it?
get()
return null
? This has to do with the fact that Task
is, fundamentally, an implementation of Runnable
. It is not an implementation of Callable
. Therefore, you are calling #submit(Runnable)
which returns a Future<?>
1 , meaning no result is expected. A Runnable
cannot return a value. In other words, the call to #get()
will always return null
in this case.
But you should really be calling #execute(Runnable)
when passing a Task
to an ExecutorService
, anyway. There's no reason to have a Future
object representing the status of the Task
. This is for at least two reasons:
The call to Future#get()
is a blocking call . The whole purpose of Task
is to communincate a result specifically back to the JavaFX Application Thread . And you must never block that thread, as doing so will lead to an unresponsive UI.
A Task
is a FutureTask
2 , which means it is already a Future
. If you really need to wait for a result ( not on the FX thread ), then you can just call #get()
on the Task
instance.
1. It really should have been defined to return Future<Void>
2. Note that the standard ExecutorService
implementation, ThreadPoolExecutor
, wraps all submitted Runnable
and Callable
objects in a FutureTask
, at least by default.
Task
As noted earlier, the purpose of Task
(and the other javafx.concurrent
classes) is to offload work to a background thread but communicate a result (as well as messages, progress, etc.) back to the JavaFX Application Thread . That thread specifically, not any other thread. But you must not block the FX thread. That means observing the task for completion, not waiting for it to complete. Then when it does complete, you react by doing what needs doing.
But how to get the value from a completed Task
? You query its value
property, which will be set to whatever is returned by the #call()
method if and when the task succeeds. You can directly observe this property with a listener, if you want. Personally, I prefer using the onSucceeded
and onFailed
properties. For example:
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);
Note properties of Task
like message
, progress
, value
and so on are guaranteed to only be set by the FX thread. And the onXXX
handlers are also guaranteed to be invoked only by the FX thread.
See Concurrency in JavaFX and javafx.concurrent
documentation for more information.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.