JavaFX WebEngine JavaScript Upcalls

В яве Windows 10 pro x64 jre 1.8.0_60 следующий код производит намеченный вывод (после нажатия кнопки html):

Hello World

Однако в Java Windows 10 pro x64 jre 1.8.0_152 кажется, что происходит какое-то разъединение, так как при нажатии кнопки ничего не выводится на консоль

Почему в последней версии Java (в то время 152) мой код дает непредсказуемые и обычно нежелательные результаты. Я попытался дать минимальный код для создания сценария ниже.

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class Main extends Application {

public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) {
    WebView browser = new WebView();
    final WebEngine webEngine = browser.getEngine();
    webEngine.setJavaScriptEnabled(true);
    webEngine.load("https://api.ipify.org/?format=json");
    webEngine.getLoadWorker().stateProperty().addListener(
            new ChangeListener<Worker.State>() {
                @Override
                public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        JSObject jso = (JSObject) webEngine.executeScript("window");
                        webEngine.executeScript(
                                "var button = document.createElement(\"button\");\n" +
                                        "button.innerHTML = \"Do Something\";\n" +
                                        "var body = document.getElementsByTagName(\"body\")[0];\n" +
                                        "body.appendChild(button);\n" +
                                        "button.addEventListener (\"click\", function() {java.exit();});");
                        jso.setMember("java", new Bridge());

                    }
                }
            });
    BorderPane panel = new BorderPane(browser);
    Scene scene = new Scene(panel, 700, 700);
    primaryStage.setScene(scene);
    primaryStage.show();
}

public class Bridge {
    public void exit() {
        System.out.println("Hello World");
    }
}}

1 ответ

Решение

Вы проходите new Bridge() к методу setMember. Так как ни одна переменная не содержит экземпляр Bridge, она собирается сборщиком мусора еще до того, как вы нажмете кнопку.

Из документации WebEngine:

Обратите внимание, что в приведенном выше примере приложение содержит ссылку на JavaApplication пример. Это необходимо для обратного вызова из JavaScript для выполнения желаемого метода.

В следующем примере приложение не содержит ссылку на объект Java:

JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());

В этом случае, поскольку значение свойства является локальным объектом, "new JavaApplication()", значение может быть мусором, собранным в следующем цикле GC.

Когда пользователь щелкает ссылку, это не гарантирует выполнение метода обратного вызова. exit,

Держите объект Bridge в поле, чтобы предотвратить его сборку:

new ChangeListener<Worker.State>() {
    private final Bridge bridge = new Bridge();

    @Override
    public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
        if (newState == Worker.State.SUCCEEDED) {

            // ...

            jso.setMember("java", bridge);
        }
    }

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

Другие вопросы по тегам