日別アーカイブ: 2013年12月24日

JavaFXの非同期処理中に画面を閉じたら。。。。

バックグラウンドでタスクを動かしている状態でJavaFXの画面を閉じる。

閉じる

すると、裏で走っていたTaskが終わった後もなんか生き残っている。

残骸

 

環境は1.7.0_45に入っているjfxrt.jarでEclipse実行。

ソースは下の感じ。

    @FXML
    private void handleExecAction(ActionEvent event) {
        this.logger.info("start");
        try {
            analizeTask = getAnalizeTask();
            pgBar.progressProperty().unbind();
            pgBar.progressProperty().bind(analizeTask.progressProperty());
            pgLabel.textProperty().bind(analizeTask.messageProperty());
            final ExecutorService exe = Executors.newSingleThreadExecutor();
            exe.submit(analizeTask);

    private Task<String> getAnalizeTask() {
        return new Task<String>() {
            @Override
            protected String call() throws Exception {
                logger.info("start");
                try {

 

とりあえずは、

  1. Applicationクラスにコントローラー持たしておいて
  2. stopメソッドからコントローラーの終了メソッドを呼んで
  3. その中からTaskのcancel呼んで
  4. Taskのキャンセルイベントのハンドラー内で強制終了かけてやる

で回避。

Task内の終了処理をきちんとやろうとするといろいろはまりそうですが。

 

1

    private FXMLController controller;

    @Override
    public void start(Stage stage) throws Exception {

        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/Scene.fxml"));
        Parent root = (Parent) fxmlLoader.load();
        controller = (FXMLController) fxmlLoader.getController();

2

    @Override
    public void stop() throws Exception {
        controller.destroy();
    }

3

public class FXMLController implements Initializable {    
    public void destroy() {
        if (analizeTask != null)
            analizeTask.cancel();
    }

4

            analizeTask.addEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED,
                    new EventHandler<WorkerStateEvent>() {
                        @Override
                        public void handle(WorkerStateEvent t) {
                            exe.shutdown();
                        }
                    });

JavaFXでFXMLを使った際のApplicationからcontrollerへのアクセス

FXMLクラスで”fx:controller”(シーンビルダーだとデフォルトで入っているはず)を指定しておいてアプリケーションクラスでFXMLLoader#getControllerを呼べばコントローラを取得できるのであとはごにょごにょすればOK。

注意点は、FXMLLoader#loadを呼んだ後でないとFXMLLoader#getControllerが取得できない(nullになる)こと。

 

(FXML)

<AnchorPane id="AnchorPane" prefHeight="427.0" prefWidth="423.5" 
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" 
fx:controller="jp.co.epea.FXMLController">
    <children>
        <VBox fx:id="vBox"

 

(Applicationクラス)

public class MainApp extends Application {

  private FXMLController controller;

    @Override
    public void start(Stage stage) throws Exception {

        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/Scene.fxml"));
        Parent root = (Parent)fxmlLoader.load();
        controller = (FXMLController) fxmlLoader.getController();