I'm trying to create an application in JavaFX that would retrieve data from my SQLITE database and display it. But even if I tried to use Task and Platform.runLater() and simulate a loading using Thread.sleep(), it would still freeze my app. I'm new to javaFX concurrency, but I'm trying to read the documentation (JavaFXDocs) but can't seem to find my solution.
Here's the code
@FXML
private void showAllData() throws SQLException, IOException {
Task<Void> task = dataShowAsATask();
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private Task<Void> dataShowAsATask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
Platform.runLater(() -> {
try {
clearDataResultHolder();
dataResultHolder.getChildren().addAll(getArrayOfUserAsNode());
/* Simulate loading */
Thread.sleep(2 * 1000);
} catch (SQLException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
});
return null;
}
};
}
private Node getUserAsANode(User user) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/prince/firstcrudtest/views/card/CardSkeleton.fxml"));
Node data = fxmlLoader.load();
CardSkeletonController controller = fxmlLoader.getController();
setController(controller, user);
return data;
}
private AbstractList<Node> getArrayOfUserAsNode() throws SQLException, IOException {
ArrayList<Node> result = new ArrayList<>();
ArrayList<User> data = dbNav.getAllData();
for (User user : data) {
result.addLast(getUserAsANode(user));
}
return result;
}
private void setController(CardSkeletonController controller, User user) {
controller.setIdLabel(String.valueOf(user.getId()));
controller.setUsernameLabel(user.getUserName());
controller.setPasswordLabel(Arrays.toString(user.getPassword()));
controller.setSaltLabel(Arrays.toString(user.getSalt()));
}
private void clearDataResultHolder() {
dataResultHolder.getChildren().clear();
}
I really don't know what to do because I'm new to JavaFX and Java Threading.
The reason for doing time-consuming operations in a thread which is not the JavaFX application thread is that they would delay the processing of JavaFX events, including rendering scenes and processing mouse and keyboard input.
Platform.runLater exists specifically for the purpose of running a task in the JavaFX application thread. Using it to update your scene (which is what clearDataResultHolder() and dataResultHolder.getChildren().addAll(getArrayOfUserAsNode()) do) is correct.
However, calling Thread.sleep in the JavaFX application thread is detrimental. That is what is freezing your application.
Creating a new thread based on a Task that does long-running operations is good, because that new thread is not the JavaFX application thread and will not hang the application thread. You should be executing Thread.sleep in that task before calling Platform.runLater:
If you were actually performing database operations, instead of just calling Thread.sleep, the code structure would be the same: do the work in the Task’s thread, then call Platform.runLater to update the scene with the data you’ve retrieved.