multithreading - Java FXML Updating UI with new Thread throws error -


i'm trying periodically update google maps marker in fxml. tried timer , new thread, can't of work.

i tested new thread simple task update textfield in ui, works fine.

however, when use actual code need update map:

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

i output message:

exception in thread "thread-26" java.lang.illegalstateexception: not on fx application thread; currentthread = thread-26     @ com.sun.javafx.tk.toolkit.checkfxuserthread(toolkit.java:236)     @ com.sun.javafx.tk.quantum.quantumtoolkit.checkfxuserthread(quantumtoolkit.java:423)     @ javafx.scene.web.webengine.checkthread(webengine.java:1216)     @ javafx.scene.web.webengine.executescript(webengine.java:980)     @ de.fkfs.v2x.eval.fxmldocumentcontroller$1.run(fxmldocumentcontroller.java:84=) 

a similar thing happens when use timer, works task of updating label, if try update marker position throws message:

exception in thread "timer-0" java.lang.illegalstateexception: not on fx application thread; currentthread = timer-0 

updates ui, including calls webengine.executescript(...) must executed on fx application thread.

on other hand, fx application thread (effectively) thread used rendering ui , processing user input. if block thread infinite loop, or other long running process, or if schedule many things run on thread, make ui unresponsive.

what trying in code appears to update ui fast can. if put loop in fx application thread block entirely: if put on background thread , schedule updates using platform.runlater(...) flood fx application thread many updates , prevent doing usual work, , become unresponsive.

the general solution here revolves around fact it's redundant update ui often. human eye can detect visible changes @ limited rate, , in technology terms limited by, e.g. refresh rate of physical screen , of underlying graphical software. javafx attempts update ui @ no more 60hz (in current implementation). there's no point in updating more underlying javafx toolkit updates scene.

the animationtimer provides handle method guaranteed invoked once per scene update, no matter how occurs. animationtimer.handle(...) invoked on fx application thread, can safely make changes ui here. implement tracking with:

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

the thing wary of here that, because handle() invoked on fx application thread, should not perform long-running code here. looks though fileimport.getgpsposition() method performs io operations, should delegated background thread. trick here, 1 used javafx classes such task, continually update value background thread, , only schedule call platform.runlater(...) if 1 not pending.

first, define simple class representing location (make immutable thread-safe):

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

and now:

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

the way works creates (thread-safe) holder current location, , updates fast possible. when updates it, (atomically) checks if current value null. if it's null, schedules ui update via platform.runlater(). if not, updates value schedules no new ui update.

the ui update (atomically) gets current (i.e. recent) value , sets null, indicating ready receive new ui update. processes new update.

this way "throttle" ui updates new ones scheduled when current 1 being processed, avoiding flooding ui thread many requests.


Comments

Popular posts from this blog

magento2 - Magento 2 admin grid add filter to collection -

Android volley - avoid multiple requests of the same kind to the server? -

Combining PHP Registration and Login into one class with multiple functions in one PHP file -