2016-07-28 3 views
0

私はFXMLのGoogleマップマーカーを定期的に更新しようとしています。私はタイマーと新しいThreadでこれをしようとしましたが、それを動作させることはできません。Java FXML新しいスレッドスローエラーでUIを更新する

新しいThreadを私のUIのTextFieldを更新する簡単なタスクでテストしましたが、うまくいきます。

しかし、私はマップ更新する必要があり、実際のコードを使用する場合:

@FXML 
public void handleTracking() throws IOException, InterruptedException { 
    new Thread() { 
     @Override 
     public void run() { 
      while (true) { 
       try { 
        double ar[] = FileImport.getGpsPosition(); 
        System.out.println("Latitude: " + ar[0] + " Longitude: " + ar[1]); 
        double Ltd = ar[0]; 
        double Lng = ar[1]; 
        webEngine.executeScript("" 
      + "window.lat = " + Ltd + ";" 
      + "window.lon = " + Lng + ";" 
      + "document.goToLocation(window.lat, window.lon);"); 
        try { 
         Thread.sleep(555); 
        } catch (InterruptedException ex) { 
         Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); 
        } 
       } catch (IOException ex) { 
        Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); 
       } 
      } 
     } 
    }.start(); 
} 

私は、出力メッセージを取得する:

Exception in thread "Thread-26" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-26 
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236) 
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423) 
    at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216) 
    at javafx.scene.web.WebEngine.executeScript(WebEngine.java:980) 
    at de.fkfs.v2x.eval.FXMLDocumentController$1.run(FXMLDocumentController.java:84=) 

私はタイマーを使用するときに同じようなことが起こります、ラベルを更新する作業のために機能しますが、マーカー位置を更新しようとすると、メッセージをスローします。

Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = Timer-0 
+2

[java.lang.IllegalStateExceptionの可能な複製:FXアプリケーションスレッドではありません。 currentThread = Thread-4](http://stackoverflow.com/questions/29449297/java-lang-illegalstateexception-not-on-fx-application-thread-currentthread-t) – DVarga

+1

UIを更新する例は、ここで別のスレッド:http://stackoverflow.com/documentation/javafx/2230/threading/7291/updating-the-ui-using-platform-runlater#t=201607280912032640534 – fabian

答えて

1

webEngine.executeScript(...)への呼び出しを含むUIの更新は、FXアプリケーションスレッドで実行する必要があります。

一方、FXアプリケーションスレッドは、UIのレンダリングとユーザー入力の処理に使用されるスレッドです(実質的に)。だから、このスレッドを無限ループやその他の長時間実行しているプロセスでブロックするか、あまりにも多くの処理をスケジュールしてそのスレッドを実行すると、UIが応答しなくなります。

あなたのコードで何をしようとしているかはできるだけ早くUIを更新するようです。ループをFXアプリケーションスレッドに入れると、完全にブロックされます:バックグラウンドスレッドに入れて、Platform.runLater(...)を使用して更新をスケジュールすると、FXアプリケーションスレッドにあまりにも多くの更新が適用され、それは応答しなくなるでしょう。

ここでの一般的な解決策は、UIを頻繁に更新することは実際には冗長であるという事実を中心にしています。人間の目は、目に見える変化を限られた速度でしか検出できません。技術的には、制限されています。物理スクリーンと基礎となるグラフィカルソフトウェアのリフレッシュレート。 JavaFXは、現在の実装ではUIを60Hz以下で更新しようとします。基本的なJavaFXツールキットがシーンを更新するよりも頻繁に更新する必要はありません。

AnimationTimerは、発生頻度にかかわらず、シーン更新ごとに1回呼び出されることが保証されているhandleメソッドを提供します。 FXアプリケーションスレッドでAnimationTimer.handle(...)が呼び出されるので、ここでUIを安全に変更できます。だから、あなたとあなたの追跡を実現することができます。

private AnimationTimer tracker ; 

public void initialize() { 
    tracker = new AnimationTimer() { 
     @Override 
     public void handle(long timestamp) { 

      try { 
       double ar[] = FileImport.getGpsPosition(); 
       // System.out.println("Latitude: " + ar[0] + " Longitude: " + ar[1]); 
       double Ltd = ar[0]; 
       double Lng = ar[1]; 
       webEngine.executeScript("" 
     + "window.lat = " + Ltd + ";" 
     + "window.lon = " + Lng + ";" 
     + "document.goToLocation(window.lat, window.lon);"); 
      } catch (IOException ex) { 
       Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); 
      } 

     } 
    }; 
} 

@FXML 
public void handleTracking() { 
    tracker.start(); 
} 

こちらを警戒すべき唯一のことは、handle()がFXのアプリケーションスレッドで呼び出されているので、あなたはここで任意の実行時間の長いコードを実行してはならない、ということです。あなたのFileImport.getGpsPosition()メソッドがいくつかのIO操作を実行するかのように見えるので、おそらくバックグラウンドスレッドに委譲されるべきです。ここでは、TaskのようなJavaFXクラスで使用されるトリックは、バックグラウンドスレッドから値を継続的に更新することであり、のみはまだ保留中でない場合はPlatform.runLater(...)への呼び出しをスケジュールします。今

class Location { 
    private final double longitude ; 
    private final double latitude ; 

    public Location(double longitude, double latitude) { 
     this.longitude = longitude ; 
     this.latitude = latitude ; 
    } 

    public double getLongitude() { 
     return longitude ; 
    } 

    public double getLatitude() { 
     return latitude ; 
    } 
} 

と::

@FXML 
private void handleTracking() { 

    AtomicReference<Location> location = new AtomicReference<>(null); 

    Thread thread = new Thread(() -> { 
     try { 
      while (true) { 
       double[] ar[] = FileImport.getGpsPosition(); 
       Location loc = new Location(ar[0], ar[1]); 

       if (location.getAndSet(loc) == null) { 
        Platform.runLater(() -> { 
         Location updateLoc = location.getAndSet(null); 
         webEngine.executeScript("" 
          + "window.lat = " + updateLoc.getLatitude() + ";" 
          + "window.lon = " + updateLoc.getLongitude() + ";" 
          + "document.goToLocation(window.lat, window.lon);"); 
        }); 
       } 
      } 
     } catch (IOException exc) { 
      Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    }); 

    thread.setDaemon(true); 
    thread.start(); 
} 

これが機能する方法があることである

まず、ちょうど場所(それは不変、それがスレッドセーフになるようにする)を表現するための単純なクラスを定義します現在の場所の(スレッドセーフな)ホルダーを作成し、できるだけ早く更新します。更新すると、現在の値がnullかどうかも(アトミックに)チェックされます。 nullの場合は、Platform.runLater()でUI更新をスケジュールします。そうでない場合は、単に値を更新しますが、新しいUIの更新はスケジュールされません。

UIアップデート(アトミック)は現在の(つまり最新の)値を取得し、nullに設定して、新しいUIアップデートを受け取る準備ができたことを示します。その後、新しい更新を処理します。

この方法では、現在のUIの処理中に新しいものがスケジュールされるようにUI更新を絞り込み、UIスレッドにあまりにも多くのリクエストがあふれないようにします。

+0

James_Dさんありがとう!両方の技術が動作します。あなたが正しいです、私はおそらく私のログファイルから新しいGPSデータを取得したいと思っています。私はそれを完全に理解するためにあなたのコードを数回通らなければならないでしょう。ありがとう、本当にありがとう! – Evo

0

すべてのJavaFX UI要素は、FXアプリケーションスレッド内で更新する必要があります。

追加のスレッドを使用する場合は、必ずplatform.Runlater()を使用してUI要素を更新してください。

+0

プラットフォームで実行されるコードをラップすると。 runLaterでは、プログラムは実行されていますが、マップがスクロールできないほど遅くなっています。 – Evo

+0

@Evo代わりにタイムラインを使用しないでください!それはずっと簡単です! –

+0

@Evoあなたのwhileloopがプラットフォームのrunlaterメソッドにラップされていることを確認してください!私は意味した! –

関連する問題