Obecnie pracuję nad aplikacją opartą na JavaFX, w której użytkownicy mogą wchodzić w interakcję z miejscami oznaczonymi na mapie świata. Aby to zrobić, używam podejścia podobnego do opisanego w http://captaincasa.blogspot.de/2014/01/javafx-and-osm-openstreetmap.html ([1]).Wywołanie zwrotne JavaFx WebView z błędu Javascript po pobraniu śmieci
Mam jednak do czynienia z trudnym do debugowania problemem związanym ze zmienną zwrotną JavaScript wstrzykniętą na osadzoną stronę HTML przy użyciu metody setMember() WebEngine (patrz także https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/js-javafx.htm ([2]) w celu uzyskania oficjalnego samouczka) .
Podczas uruchamiania programu przez pewien czas zmienna wywołania zwrotnego traci nieprzewidywalny stan! Aby zademonstrować to zachowanie, opracowałem minimalny przykład pracy/awarii. Używam 64-bitowego jdk1.8.0_121 na komputerze z systemem Windows 10.
JavaFX aplikacji wygląda następująco:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.application.Application;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class WebViewJsCallbackTest extends Application {
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
public static void main(String[] args) {
launch(args);
}
public class JavaScriptBridge {
public void callback(String data) {
System.out.println("callback retrieved: " + data);
}
}
@Override
public void start(Stage primaryStage) throws Exception {
WebView webView = new WebView();
primaryStage.setScene(new Scene(new AnchorPane(webView)));
primaryStage.show();
final WebEngine webEngine = webView.getEngine();
webEngine.load(getClass().getClassLoader().getResource("page.html").toExternalForm());
webEngine.getLoadWorker().stateProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("javaApp", new JavaScriptBridge());
}
});
webEngine.setOnAlert(event -> {
System.out.println(DATE_FORMAT.format(new Date()) + " alerted: " + event.getData());
});
}
}
Plik HTML "page.html" wygląda następująco:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<!-- use for in-browser debugging -->
<!-- <script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> -->
<script type="text/javascript">
var javaApp = null;
function javaCallback(data) {
try {
alert("javaApp=" + javaApp + "(type=" + typeof javaApp + "), data=" + data);
javaApp.callback(data);
} catch (e) {
alert("caugt exception: " + e);
}
}
</script>
</head>
<body>
<button onclick="javaCallback('Test')">Send data to Java</button>
<button onclick="setInterval(function(){ javaCallback('Test'); }, 1000)">Send data to Java in endless loop</button>
</body>
</html>
Stan zwrotnego zmiennej javaApp
można zaobserwować klikając przycisk "Send data to Java in endless loop"
. Będzie stale próbował uruchomić metodę wywołania zwrotnego za pośrednictwem javaApp.callback
, która generuje komunikat logowania w aplikacji Java. Alarmy są używane jako dodatkowy kanał komunikacyjny do podtrzymania działania (zawsze wydaje się, że działa i jest obecnie używany jako obejście, ale to nie jest tak, jak powinno być ...).
Jeśli wszystko działa tak jak powinien, za każdym razem zalogowaniu podobne do następujących linii powinna zostać wydrukowane:
callback retrieved: Test
2017/01/27 21:26:11 alerted: [email protected]c693(type=object), data=Test
Jednak po pewnym czasie (wszystko od 2-7 minut), nie więcej wywołania zwrotne są pobierane, ale tylko Loggings jak następującej linii są drukowane:
2017/01/27 21:32:01 alerted: javaApp=undefined(type=object), data=Test
Drukowanie zmienną daje teraz 'undefined'
zamiast instancji ścieżki Java. Dziwną obserwacją jest to, że stan javaApp
nie jest naprawdę "nieokreślony". użycie typeof
zwraca object
, javaApp === undefined
ocenia na false
. Jest to zgodne z tym, że wywołanie zwrotne nie rzuca wyjątku (w przeciwnym razie wydrukowałby się alert rozpoczynający się od "caugt exception: "
).
Korzystanie z Java VisualVM pokazało, że czas awarii przypadał na czas aktywacji Garbage Collectora. Można to zaobserwować obserwując zużycie pamięci Heap, która spada z ok. 60 MB do 16 MB dzięki GC.
Co tam jest? Czy masz pojęcie, jak mogę dalej debugować problem? Nie mogłem znaleźć żadnego powiązanego z nim błędu ...
Bardzo dziękuję za radę!
PS: problem został odtworzony znacznie szybciej, gdy kod JavaScript został włączony, aby wyświetlić mapę świata za pośrednictwem ulotki (patrz [1]). Ładowanie lub przesuwanie mapy przez większość czasu natychmiast spowodowało, że GC wykonał swoje zadanie. Podczas debugowania tego oryginalnego problemu wyśledziłem problem w przedstawionym tutaj minimalnym przykładzie.
Miałem ten sam problem z jdk 1.8.0_121. Wtedy zdałem sobie sprawę, że działa znaleźć z innego komputera, który działa jdk 1.8.0_60. Przełączono na 1.8.0_60, a problem został rozwiązany. –
@AliAyadJalil: Powrót do starej wersji nie zawsze jest pożądany, a nawet możliwy. : -/ –