Есть много вопросов и ответов по поводу параллелизма, и мои могут быть похожи на другие, но для меня это не дубликат, так как по какой-то причине я что-то упускаю и надеюсь получить какой-то совет ...
У меня вопрос больше, когда мне нужна вторая пара глаз, чтобы указать, что я делаю неправильно, чтобы мой код работал в фоновом потоке, но также обновил графический интерфейс, не останавливая его.
Первоначально файл PDF загружается в приложение, используя задачу в потоке.
Это отлично работает.
Отображается индикатор выполнения, который анимируется без проблем:
uploadFile ()
public void uploadFile(File fileToProcess) {
fileBeingProcessed = fileToProcess;
Task<Parent> uploadingFileTask = new Task<Parent>() {
@Override
public Parent call() {
try {
progressBarStackPane.setVisible(true);
pdfPath = loadPDF(fileBeingProcessed.getAbsolutePath());
createPDFViewer();
openDocument();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(MainSceneController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
};
uploadingFileTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
fileHasBeenUploaded = true;
progressBarStackPane.setVisible(false);
uploadFilePane.setVisible(false);
tabPane.setVisible(true);
/* This is where I am getting issue, more so in createThumbnailPanels() */
setupThumbnailFlowPane();
createThumbnailPanels();
/****** ^^^^^^^^^^^^ ******/
}
});
uploadingFileTask.setOnFailed(evt -> {
uploadingFileTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(uploadingFileTask.getException().getSuppressed()));
});
Thread uploadingFileThread = new Thread(uploadingFileTask);
uploadingFileThread.start();
}
После загрузки документа он отображается на вкладке, которая позволяет пользователю просматривать документ.
Существует дополнительная вкладка, которая после загрузки отключается до завершения другого задания с именем createThumbnailPanelsTask
;
Однако, прежде чем выполнить это задание, создается FlowPane
для панелей миниатюр. Кажется, это работает без проблем и не является причиной зависания графического интерфейса (это явно цикл в createThumbnailPanelsTask
, но для ясности я покажу setupThumbnailFlowPane()
):
setupThumbnailFlowPane ()
public void setupThumbnailFlowPane() {
stage = model.getStage();
root = model.getRoot();
secondaryTabScrollPane.setFitToWidth(true);
secondaryTabScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
/**
This will be removed from here when refactored but for now it is here,
I don't think this is anything to do with my issue
**/
Set<Node> nodes = secondaryTabScrollPane.lookupAll(".scroll-bar");
for (final Node node : nodes) {
if (node instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) node;
if (sb.getOrientation() == Orientation.VERTICAL) {
sb.setUnitIncrement(30.0);
}
if (sb.getOrientation() == Orientation.HORIZONTAL) {
sb.setVisible(false);
}
}
}
secondaryTab = new FlowPane();
secondaryTab.setId("secondaryTab");
secondaryTab.setBackground(new Background(new BackgroundFill(Color.LIGHTSLATEGRAY, new CornerRadii(0), new Insets(0))));
secondaryTab.prefWidthProperty().bind(stage.widthProperty());
secondaryTab.prefHeightProperty().bind(stage.heightProperty());
secondaryTab.setPrefWrapLength(stage.widthProperty().intValue() - 150);
secondaryTab.setHgap(5);
secondaryTab.setVgap(30);
secondaryTab.setBorder(new Border(new BorderStroke(Color.TRANSPARENT, BorderStrokeStyle.NONE, CornerRadii.EMPTY, new BorderWidths(8, 10, 20, 10))));
secondaryTab.setAlignment(Pos.CENTER);
}
Наконец, вызывается createThumbnailPanels()
, и я полагаю, что у меня проблема.
Предполагается, что после загрузки документа панель загружаемых файлов будет скрыта, открывая вкладку Viewer, а также вкладку Secondary.
В этот момент дополнительная вкладка отключена, и на левой ее стороне также находится изображение загрузки (a gif
).
Предполагается, что задача createThumbnailPanels()
будет выполняться в фоновом режиме, и до ее завершения вкладка будет оставаться отключенной, однако в течение этого времени изображение gif
будет вращаться, создавая впечатление происходит некоторая загрузка.
После завершения загрузки gif
удаляется, и вкладка активируется, позволяя пользователю перейти к ней и просмотреть сгенерированные панели миниатюр.
Это все работает, однако, как уже упоминалось, задача вешает графический интерфейс:
createThumbnailPanels ()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
@Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex = 0; thumbIndex < numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
model.setThumbnailAt(tn, thumbIndex);
eventHandlers.setMouseEventsForThumbnails(tb);
/*
I have added this in as I am under the impression that a task runs in a background thread,
and then to update the GUI, I need to call this:
*/
Platform.runLater(() -> {
secondaryTab.getChildren().add(tb);
});
}
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
/*
Further GUI modification run in setOnSucceeded so it runs on main GUI thread(?)
*/
secondaryTabScrollPane.setContent(secondaryTab);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
Все, кроме зависания графического интерфейса, пока он создает панели, работает нормально.
После того, как они были созданы, графический интерфейс можно снова контролировать, загрузка gif
была удалена, вкладка включена, и пользователь может перейти к ней и просмотреть панели.
Ясно , здесь есть кое-что, что мне не хватает в параллелизме.
Как уже упоминалось, у меня сложилось впечатление, что Задача выполняется в фоновом потоке, поэтому меня немного смущает, почему она этого не делает. Опять же, явно что-то мне не хватает.
Я читал, и читал, и читал о параллелизме, но просто не могу понять, где в моем подходе я ошибся. У меня возникает соблазн попробовать использовать Сервис, однако я чувствую, что просто слишком усложняю ситуацию, учитывая это, и что, несомненно, существует простой способ сделать то, чего я хочу достичь.
Любая помощь будет принята с благодарностью ... толчок в правильном направлении или некоторые разъяснения о том, где я ошибся в моем понимании.
Заранее спасибо, несомненно, что-то очевидно, что однажды отсортированная поможет мне избежать этой проблемы в будущем!
ОБНОВЛЕННЫЙ КОД
createThumbnailPanels ()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
@Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex= 0; thumbIndex< numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
eventHandlers.setMouseEventsForThumbnails(tb);
model.setThumbnailAt(tn, thumbIndex);
model.setThumbnailPanels(tb);
}
setThumbnailPanelsToScrollPane();
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
// setThumbnailPanelsToScrollPane();
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
setThumbnailPanelsToScrollPane ()
public void setThumbnailPanelsToScrollPane() {
Task<Void> setThumbnailPanelsToScrollPaneTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
@Override
public Void call() {
Platform.runLater(() -> {
secondaryTab.getChildren().addAll(model.getThumbnailPanels());
secondaryTabScrollPane.setContent(main.informationExtractionPanel);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
});
return null;
}
};
setThumbnailPanelsToScrollPaneTask.setOnFailed(evt -> {
setThumbnailPanelsToScrollPaneTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(setThumbnailPanelsToScrollPaneTask.getException().getSuppressed()));
});
Thread setThumbnailPanelsToScrollPaneThread = new Thread(setThumbnailPanelsToScrollPaneTask);
setThumbnailPanelsToScrollPaneThread.start();
}
К вашему сведению: если я позвоню setThumbnailPanelsToScrollPane();
в setOnSucceeded, он не будет работать.