2016-12-23 6 views
0

私はjavafxプログラムを作成しています。プログラムのナビゲーションメニューは、シーンのルートを変更することで機能します。ルーツはすべてPaneクラスから継承しています。ペインのいくつかは、実行するバックグラウンドスレッドを持っています。しかし、イベントハンドラによってルートペインが変更されると、ペインは切り替わりますが、バックグラウンドスレッドは停止しません。これは、スレッドがNFCを読み取り、複数のスレッドがNFCリーダーからの読み取りを試みるため、問題を引き起こします。
バックグラウンドスレッドを閉じるにはどうしたらいいですか? (作成されたペインの外側から)、または別の方法でスレッドを設定する必要がありますか。 (スレッドはデーモンに設定されています)。 スレッドは、次のようにPaneコンストラクタで作成されます: (私は、ウィンドウが切り替わったときにウィンドウに属していたと仮定していましたが、スレッドは停止します。背景ルートノードが変更されたときにスレッドが閉じない[javaFX]

Runnable r = new Runnable() { 
     @Override 
     public void run() { 
      boolean cont = true; 
      while(cont){ 

       try { 
        NFCcard create1 = new NFCcard(); 

        String staffID=create1.getCardID().toString(); 
        staffID = staffID.replaceAll("\\D+",""); 
        signIn.setText("Welcome "+getUserName(staffID)+getPhotoSrc(create1.getCardID().toString())); 

        Thread.sleep(1000); 

       } catch (CardException e) { 


       } catch (InterruptedException e) { 

       } 
       signIn.setText("Scan your card to sign in/out"); 
       if(getScene().getRoot().isDisable()); 
        cont=false; 
      } 

      }}; 

    Thread nfcCheckThread = new Thread(r); 
    nfcCheckThread.setDaemon(true); 
    nfcCheckThread.start(); 

私はこのような静的な方法でペインを切り替えます(この方法が自分のクラスの中にあります)。

public static void homeButtonhandler(Stage stage){ 
    HomePane mainPane1=new HomePane(stage, new HomeContent(stage)); 
    stage.getScene().setRoot(mainPane1); 
    } 
public static void adminButtonhandler(Stage stage){ 

     DialogBox dialog = new DialogBox(); 

     try{ 
     Optional<String> result = dialog.showAndWait(); 
     if (result.get().equals("115")){ 
      AdminPane adminPane1 = new AdminPane(stage,new Content(stage)); 
      stage.getScene().setRoot(adminPane1); 
      }} 

     catch(NoSuchElementException Exception){ 

     } 


} 

public static void workingTodayButtonhandler(Stage stage){ 
    //TODO trying to make the content change when when buttons are clicked 

    HomePane mainPane2=new HomePane(stage,new WorkingTodayContent(stage)); 

    stage.getScene().setRoot(mainPane2); 
// System.out.println(mainPane2.content); 

} 

まず、呼び出しは次のとおりです。

HomePane myPane =新しいHomePane(primaryStage、新しいHomeContent(primaryStage));

Scene homeScene = new Scene (myPane); 

    primaryStage.setMinHeight(1000); 
    primaryStage.setMinWidth(1700); 

    primaryStage.setScene(homeScene); 


    primaryStage.show(); 
+0

デーモンスレッドは、jvmが終了するかメインが戻るときに終了します。早期に終了したい場合は、手動で終了する必要があります。これを行う標準的な方法は、backroundスレッドに定期的に "keepGoing"フラグをチェックさせることです。このフラグは、終了させるときに解除することができます。 –

+0

デーモンスレッドであるスレッドは、ランタイムのシャットダウンを防止しない限り、何の効果もありません。これらのスレッドを自分でシャットダウンするためのロジックを実装する必要があります...リスナーから 'Thread'を確認できる' Scene'ルートに更新された 'AtomicReference'を使うことができます。シーンのルートがあなたのデザインに関する情報の量が少ないので、何かをお勧めするのは難しいです... Executorサービスを使用すると、btwを助けることができます。 – fabian

+0

スレッドの作成方法が表示されない場合はあなたのコードで閉じている場合、別の方法でスレッドを設定する必要があるかどうかは言えません。あなたの状況を再現するいくつかの[最小、完全、および検証可能なコード](http://stackoverflow.com/help/mcve)を提供してください。 –

答えて

0

は、ペインのいくつかは、彼らが実行するバックグラウンドスレッドを持っている別のスレッドからの更新のためのPlatform.runLaterを使用してください。

私はあなた自身のPane実装でこれらのスレッドを作成すると仮定します。抽象クラスを持つソリューションを紹介します。それぞれのペイン(または少なくともルートとして挿入するもの)を拡張する必要があります。それがない場合は、そうすることを強くお勧めします。あなたはこれらのペインについて多くの情報を提供していませんでしたので、私はあなたのコードに自分の答えを統合する作業を任せます。

public abstract class OwnPane extends Pane { 
    protected volatile boolean isRoot = false; 

    public void setAsRoot(){ 
     isRoot = true; 
    } 

    public void unsetAsRoot(){ 
     isRoot = false; 
    } 
} 

私はvolatileというキーワードに後で戻ってきます。

今、あなたは、例えばactivateThread -methodに、好ましくはOwnPaneまたはそのサブクラスでは、あなたのスレッドを作成することができます:フィールド:volatileキーワードが使用されなければならない理由は今、私が説明することができ

public void activateNFCThread(){ 
    Runnable r = new Runnable(){ 
      @Override 
      public void run() { 
       while(isRoot){ 
         // what the thread has to do ... 
       } 
      } 
    }; 
    Thread nfcCheckThread = new Thread(r); 
    nfcCheckThread.setDaemon(true); 
    nfcCheckThread.start(); 
} 

isRootは異なるスレッドによって使用されます。 volatileキーワードでは、すべてのスレッドが同じ "変数"にアクセスすることを確認します(そうしないと、パフォーマンス上の理由から各スレッドに独自のバージョンが割り当てられます)。
OwnPane(またはサブクラス)のメソッドでスレッドが作成されるため、Runnableオブジェクト内のisRootフィールドにアクセスできます。 OwnPaneのサブクラスでは、その後も、(必要な場合)setAsRootメソッドが呼び出されたときにNFSスレッドが直接起動させるためにsetAsRootメソッドをオーバーライドすることができます。

public class PaneWithNFCReader extends OwnPane { 
    @Override 
    public void setAsRoot(){ 
     super.setAsRoot(); 
     activateNFCThread(); 
    } 
} 

最後に、あなたはのルート区画を変更するには、これらのメソッドを使用することができますシーン内のステージ:

// All your methods regarding stage changes are static, so I'll leave this one static too 
public static void changeRoot(Stage stage, OwnPane newRoot){ 
    OwnPane oldStage = (OwnPane)stage.getScene().getRoot(); 
    oldStage.unsetAsRoot(); 
    Platform.runLater(() -> { //Platform.runLater to be sure the main thread that will execute this 
           //(only main thread is allowed to change something in the JavaFX nodes) 
     stage.getScene().setRoot(newRoot); 
     newRoot.setAsRoot(); 
    }); 
} 
0

シーンルートを置き換えるクラスにリスナーを登録する方法を追加する必要があります。これにより、そのような変更を通知され、スレッドを終了させることによって対応することができます。

private Parent root; 
private NodeReplaceListener listener; 
private Scene scene; 

public void setRoot(Parent root, NodeReplaceListener listener) { 
    if (root != this.root && this.listener != null) { 
     this.listener.onNodeReplace(); 
    } 
    this.root = root; 
    this.listener = listener; 

    scene.setRoot(root); 
} 

@Override 
public void start(Stage primaryStage) { 
    Button btn = new Button("Next Scene"); 
    btn.setOnAction((ActionEvent event) -> { 
     // replace root 
     setRoot(new StackPane(new Rectangle(100, 100)), null); 
    }); 

    StackPane root = new StackPane(); 
    root.getChildren().add(btn); 

    scene = new Scene(new Group(), 100, 100); 

    class MyRunnable implements Runnable { 

     // running volatile to guarantee that visibility of written values to all threads 
     volatile boolean running = true; 

     int i; 

     @Override 
     public void run() { 
      while (running) { 
       System.out.println(i++); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException ex) { 
       } 
      } 
     } 

     public void cancel() { 
      running = false; 
     } 

    } 

    MyRunnable r = new MyRunnable(); 
    Thread t = new Thread(r); 
    t.setDaemon(true); 
    t.start(); 

    primaryStage.setScene(scene); 

    // cancel runnable when root is replaced 
    setRoot(root, r::cancel); 
    primaryStage.show(); 
} 

@FunctionalInterface 
public interface NodeReplaceListener { 

    public void onNodeReplace(); 

} 
BTW:シーンの変更は、任意のスレッドが、アプリケーションスレッドから作られるべきでないことに留意されたいです。あなたが言ったように

Platform.runLater(() -> signIn.setText("Scan your card to sign in/out")); 
関連する問題