2016-12-23 2 views
0

Я создаю программу javafx, и мое меню навигации для программы работает, изменяя корень сцены. Корни все наследуются от класса Pane. Некоторые из панелей имеют фоновые потоки, которые они запускают. Но когда корневая панель изменяется обработчиками событий, панель переключается, но фоновые потоки не останавливаются. Это вызывает проблемы, когда потоки читают NFC и приводит к тому, что несколько потоков пытаются читать из NFC-считывателя.
Как закрыть фоновый поток? (извне панели, где они созданы), или мне нужно настроить потоки другим способом. (Потоки настроены на Daemon). Нити создаются в конструкторе панели как показано ниже: (Я предположил, что, поскольку они принадлежали панели, когда панель переключается, поток останавливается. Это не так).Фон Темы, которые не закрываются при изменении корневого узла [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, новый ДомМатериалы (primaryStage));

Scene homeScene = new Scene (myPane); 

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

    primaryStage.setScene(homeScene); 


    primaryStage.show(); 
+0

Демон-потоки выходят, когда jvm выходит или возвращается. Если вы хотите выйти раньше, вам нужно закрыть их вручную. Стандартный способ выполнения этого заключается в том, чтобы регулярные обратные потоки проверяли флаг «keepGoing», который вы можете отключить, когда хотите, чтобы они выходили. –

+0

Нити, являющиеся потоками деамонов, не имеют никакого эффекта, за исключением того, что они не предотвращают выключение среды выполнения. Вам нужно реализовать некоторую логику для того, чтобы закрыть эти потоки самостоятельно ... Вероятно, вы могли бы использовать некоторый «AtomicReference», обновленный от слушателя, до корня «Scene», который позволяет 'Thread' проверять, если корень сцены все еще «свой» корень и заканчивается иначе, но сложно что-либо рекомендовать, поскольку объем информации о вашем дизайне мал ... Использование служб-исполнителей может помочь btw ... – fabian

+0

Если вы не покажете, как создаются потоки и закрыты в вашем коде, мы не можем сказать, нужно ли настраивать потоки иначе или нет! Предоставьте несколько [Минимальный, Полный и Подтверждаемый код] (http://stackoverflow.com/help/mcve), который воссоздает вашу ситуацию. –

ответ

0

Как вы сказали:

Некоторые из Panes имеет фоновые потоки, которые они работают.

Предполагается, что вы создадите эти потоки в своих собственных реализациях Pane. Я покажу вам решение с абстрактным классом, который должен расширить каждый из ваших панелей (или, по крайней мере, те, которые вы вставляете в качестве корня). Если у вас этого нет, я настоятельно рекомендую вам это сделать. Однако вы не предоставили много информации об этих панелях, поэтому я оставлю вам задачу интегрировать свой ответ в ваш код (если когда-либо вы решите следовать ему).

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

    public void setAsRoot(){ 
     isRoot = true; 
    } 

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

войду backt к volatile ключевому слову позже.

Теперь вы можете создать свой поток, предпочтительно в OwnPane или подкласса этого, в activateThread -метода, например:

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

Теперь я могу объяснить, почему volatile ключевое слово должно использоваться: поле isRoot будет использоваться различными потоками. С ключевым словом volatile мы гарантируем, что все потоки будут обращаться к одной и той же «переменной» (иначе каждый поток будет иметь свою собственную версию по соображениям производительности).
Тот факт, что поток создается в методе в OwnPane (или подклассе), позволяет получить доступ к полю из объекта Runnable. В подклассах 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

Вы должны добавить способ регистрации слушателя классу, ответственному за замену корня сцены. Это позволяет вам получать уведомления об этих изменениях и реагировать, прекращая выполнение потоков.

Пример

@FunctionalInterface 
public interface NodeReplaceListener { 

    public void onNodeReplace(); 

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

Кстати: Обратите внимание, что изменения в сцене не должны быть сделаны из любого потока, но в потоке приложения. Используйте Platform.runLater для обновлений из другого потока:

Platform.runLater(() -> signIn.setText("Scan your card to sign in/out")); 
Смежные вопросы