2015-06-18 26 views
0

我有一個JavaFX應用程序在啓動時檢查是否存在數據庫。如果未找到數據庫,則必須在程序可以繼續之前創建「種子」數據庫。JavaFX啓動畫面消息和進度不更新

由於創建種子數據庫可能很長,我想要顯示啓動畫面並隨着過程的進行顯示進度更新。只有在種子數據庫完全寫入後,程序才能繼續。

下面是閃屏類:

public final class SplashWindow { 

    private final Label message; 
    private final ProgressBar progress; 
    private final Stage splashStage; 

    public SplashWindow() { 
     Image img = new Image(IMAGE_PREFIX + "splash_image.png"); 
     double imgWidth = img.getWidth(); 
     ImageView splashImage = new ImageView(img); 
     splashImage.setFitWidth(imgWidth); 
     splashImage.setPreserveRatio(true); 

     message = new Label("Saving seed database..."); 
     message.setPrefWidth(imgWidth); 
     message.setAlignment(Pos.CENTER); 

     progress = new ProgressBar(); 
     progress.setPrefWidth(imgWidth); 

     Pane splashVBox = new VBox(3); 
     splashVBox.setPadding(new Insets(5)); 
     splashVBox.getChildren().addAll(splashImage, progress, message); 

     splashStage = new Stage(StageStyle.UTILITY); 
     splashStage.setScene(new Scene(splashVBox)); 
    } 

    public void bindMessageProperty(ReadOnlyStringProperty sp) { 
     message.textProperty().bind(sp); 
    } 

    public void bindProgressProperty(ReadOnlyDoubleProperty dp) { 
     progress.progressProperty().bind(dp); 
    } 

    public void show() { 
     splashStage.show(); 
    } 

    public void shutdown() { 
     message.textProperty().unbind(); 
     progress.progressProperty().unbind(); 
     splashStage.hide(); 
    } 
} 

運行時,啓動畫面與圖像,進度條和文本消息區域正確顯示。

SplashWindow類是通過下面的方法調用:

private void saveSeedDatabase(ObservableList<RefModel> docList) { 
    SplashWindow splash = new SplashWindow(); 

    Task<Integer> saveTask = new Task<Integer>() { 
     @Override 
     protected Integer call() throws InterruptedException { 
      updateMessage("Saving references..."); 
      int docsSaved = 0; 
      for (RefModel rm : docList) { 
       if (isCancelled()) { 
        updateMessage("Cancelled"); 
        break; 
       } 

       updateMessage("Saving: " + rm.getTitle()); 
       saveNewReference(rm); 
       docsSaved++; 
       updateProgress(docsSaved, docList.size()); 
      } 
      updateMessage("Saved " + docsSaved + " references to database"); 
      return docsSaved; 
     } 
    }; 
    saveTask.setOnSucceeded((WorkerStateEvent t) -> { 
     splash.shutdown(); 
    }); 

    splash.bindMessageProperty(saveTask.messageProperty()); 
    splash.bindProgressProperty(saveTask.progressProperty()); 
    splash.show(); 

    new Thread(saveTask).start(); 
    try { 
     saveTask.get(); 
    } catch (InterruptedException | ExecutionException ex) { 
     // Do nothing 
    } 
} 

運行時,會顯示啓動畫面,但從來沒有顯示更新消息和進展。它顯示一個等待光標,當鼠標移動到閃屏上時。

如果該方法末尾的try/catch塊被註釋掉,主程序將嘗試繼續而不等待數據庫被寫入。在這種情況下,啓動畫面被主程序窗口隱藏,但它在工作時會顯示更新消息。從調試它,它看起來像一切正在正確的線程上運行 - 數據庫的東西在工作線程上,SplashWindow材料是在FX事件線程。

看起來很清楚,saveTask.get()的調用阻塞了UI線程,但我不知道爲什麼。

@JewelSea寫了一個nice alternative,它不適合我的程序架構。改變我的計劃以使用他的解決方案並不是不可能的。

但是,我不明白爲什麼get()調用阻止用戶界面。我究竟做錯了什麼。

+1

另請參閱http://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests/30250308#30250308 –

+0

@James_D:本週早些時候閱讀。非常好。感謝您的支持。 – clartaq

回答

2

按照JavaDocs

get())如有必要,等待計算完成,然後獲取其結果。

檢索計算值而不阻止GUI線程將使用getValue()完成。但是:當Task已成功完成其工作時,此方法僅返回結果。這就是爲什麼你應該在onSucceeded區塊中做這個aysnc。

+0

不,使用'getValue()'方法仍然允許程序在寫入數據庫之前繼續。我想阻止'saveSeedDatabase()'方法返回,直到任務完成並寫入數據庫,但不凍結啓動畫面的更新。 – clartaq

+2

關鍵是你在FX應用程序線程上調用'saveSeedDatabase'(你必須這樣做,因爲它創建並顯示一個'Stage')。因此,如果您在該方法中阻止(「阻止方法返回,直到任務完成」),您必須阻止FX應用程序線程。任務完成後您需要做的事情必須在'onSucceeded'處理程序中完成。 –

+0

D'oh!當然。 – clartaq