UI updates are getting blocked by future.get() in

2019-03-03 14:23发布

I have a function which is supposed to return a list from the result of a Task API.

    @Override
    public List performQuery(boolean isPaginationQuery, boolean isSortingQuery {

        try {
            TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
                    isSortingQuery);
            queryExecutor.submit(taskImpl).get();
            return taskImpl.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Inner class which performs the updates

private class TaskImpl extends Task<List> {

        private boolean isPaginationQuery, isSortingQuery;

        public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery) {
            this.isPaginationQuery = isPaginationQuery;
            this.isSortingQuery = isSortingQuery;
        }

        @Override
        protected List call() throws Exception {
             Platform.runLater(() -> {
                  loaderContainer.setVisible(true);
                  loaderContainer.toFront();
             });

            HSession hSession = new HSession();
            TaskInfoDao taskInfoDao = new TaskInfoDaoImpl(hSession.getSession(), currentConnection.getConnectionId());
            if (!isPaginationQuery && !isSortingQuery) {
                paginator.setTotal(taskInfoDao.getTaskInfoWithFiltersCount(paginator.getFilterMap(), false));
            }
            Stream<TaskInfo> resultStream = taskInfoDao.getTaskInfoWithFilters(paginator.getFilterMap(), false,
                    paginator.getStartIndex() * paginator.getPageSize(),
                    paginator.getPageSize() * paginator.getPageGap());
            List<TaskInfoTableView> data = createData(resultStream);
            hSession.close();
            return data;
        }

        @Override
        protected void succeeded() {
            super.succeeded();

            try {
                //set the pagination if the task is complete
                //and it is not a pagination query
                if (!isPaginationQuery) {
                    ((TaskInfoViewController) uiController).setPagination(
                            FXCollections.observableArrayList(get()));
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }

        @Override
        protected void cancelled() {
            super.cancelled();
            updateMessage("Cancelled!");
        }

        @Override
        protected void failed() {
            super.failed();
            updateMessage("Failed!");
        }
    }

performQuery function calls the thread and waits for its result.

The loader is being displayed from inside the TaskImpl class using Platform.runLater.
But the loader does not appear until the task has finished i.e. loader appears after the completion of call() function's execution.

When i remove the taskImpl.get() the loader works fine.

Any help is appreciated.

P.S. : Under any case, I need the result of the Task API outside the Inner class( outside TaskImpl )

1条回答
做自己的国王
2楼-- · 2019-03-03 14:43

First of all, it seems like you are not very familiar with asynchronous programming. Having performQuery() to return a List shows that you are expecting to run this synchronously - there is no way for you to return results before you get the results. This is exactly why you are freezing your UI.

The important thing to understand about asynchronous programming is, you would start doing something (i.e. a task) in another thread, and return immediately. When there is result returned from the task, you switch back to the UI (JavaFX Application) thread to update it. You can see this as event-driven approach.

Therefore, for your case, you should directly update the list (the list which you are returning in performQuery()) in the succeeded() method that you have overridden in TaskImpl class.

If the list that you should be updating is not in the scope of TaskImpl, then you can the functional interfaces in java.util.function package to do it for you. This means that you would create that functional interface object at the right scope, and pass in into TaskImpl during object construction, and call that interface in succeeded().

Update

If I assume this is what calls performQuery():

public class MyController {
    @FXML
    TableView<Foo> tableView;

    public void initialize() {
        List result = queryController.performQuery(true, true);
        tableView.getItems().addAll(result);
    }
}

Then, I would probably do something like this:

public class MyController {
    @FXML
    TableView<Foo> tableView;

    public void initialize() {
        List result = queryController.performQuery(true, true, list -> tableView.getItems.addAll(list));
    }
}

public class QueryController {
    @Override
    public void performQuery(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {

        try {
            TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
                    isSortingQuery, onQuerySucceeded);

            queryExecutor.submit(taskImpl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

private class TaskImpl extends Task<List> {

    private final java.util.function.Consumer<List> onQuerySucceeded;

    public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {
        this.isPaginationQuery = isPaginationQuery;
        this.isSortingQuery = isSortingQuery;

        this.onQuerySucceeded = onQuerySucceeded;
    }

    @Override
    protected void succeeded() {
        super.succeeded();

        // Not sure what the original codes are doing.
        try {
            //set the pagination if the task is complete
            //and it is not a pagination query
            if (!isPaginationQuery) {
                ((TaskInfoViewController) uiController).setPagination(
                        FXCollections.observableArrayList(get()));
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // This is what is being added in
        onQuerySucceeded.accept(this.getValue());
    }
}
查看更多
登录 后发表回答