簡體   English   中英

JavaFX並發性-使用在線程中運行但掛起UI的任務

[英]JavaFX Concurrency - Using a task, which runs in a thread, but hangs the UI

關於並發性有很多問題和答案,我的可能與其他類似,但是對我來說,這不是重復的,因為某些原因,我必須丟失一些東西並希望得到一些建議...

我的問題是,我需要另一雙眼睛指出我在做什么,以使我的代碼能夠在后台線程中運行,而且還更新了GUI而不凍結它。

最初,使用線程中的任務將PDF文件上載到應用程序。

這很好。

將顯示一個進度條,其動畫效果沒有問題:

上傳文件()

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任務createThumbnailPanelsTask

但是,在運行此任務之前,將創建“縮略圖面板”的FlowPane 這似乎沒有問題,並且似乎也不是GUI掛起的原因(這顯然是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() ,這是我認為遇到問題的地方。

假定發生的情況是,在文檔上載后,隱藏了上載文件窗格,其中顯示了“查看器”選項卡以及“輔助”選項卡。

此時,輔助選項卡被禁用,並且其左側還有一個加載圖像( gif )。

預期的行為是createThumbnailPanels()任務將在后台運行,並且在完成之前,該選項卡將保持禁用狀態,但是在此期間, gif圖像將旋轉,給人的印象是正在加載。

加載完成后,將刪除gif ,並啟用選項卡,從而允許用戶導航到該選項卡,並查看生成的縮略圖面板。

所有這些都有效,但是,如上所述,任務是掛起GUI:

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();
}

除非在創建面板時掛起GUI,否則一切正常。

創建它們之后,就可以再次控制GUI,刪除加載的gif ,啟用選項卡,用戶可以導航到它並查看面板。

顯然 ,在這里並發我缺少一些東西。

如前所述,我的印象是Task在后台線程中運行,所以我對為什么它似乎沒有這樣做感到困惑。 同樣,顯然我缺少了一些東西。

我已經閱讀並閱讀了有關並發的文章,但是似乎無法弄清我在哪里出錯了。 我很想嘗試使用服務,但是,我認為考慮到這只是使事情復雜化了,而且顯然有一種簡單的方法可以實現我想要實現的目標。

任何幫助將不勝感激……朝着正確的方向推進,或者對我理解上的錯誤之處進行了澄清。

在此先感謝您,毫無疑問,一旦排序,很明顯可以幫助我將來避免此問題!

更新的代碼

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中,它似乎不起作用。

getChildren().addJavaFX GUI線程上運行(這是Platform.runLater所做的事情),但是只有在您添加子對象的父級連接到所顯示gui的根目​​錄時,才需要在Platform.runLater運行它,這意味着您應該能夠將子代添加到未連接到任何根的父代,並在子代添加過程結束時將整個父代添加到根,如果您正在任何異步代碼中執行Platform.runLater在您的情況下,它將在gui線程上運行,它在異步for循環中添加ThumbnailPanels並且如果它們的數量很大,則gui將掛起。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM