JavaFX的WebEngine的JavaScript向上调用

问题描述:

在java中Windows 10 pro x64 jre 1.8.0_60下面的代码产生预期输出(点击HTML按钮后):JavaFX的WebEngine的JavaScript向上调用

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

您正在将new Bridge()传递给setMember方法。由于没有变量保存Bridge实例,所以在你按下按钮之前它会被垃圾收集。

WebEngine documentation

注意,在上面的例子中,该应用程序保持到JavaApplication实例的引用。这是从JavaScript执行所需方法的回调所必需的。

在下面的示例中,应用程序不保持到Java对象的引用:

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

在这种情况下,由于属性值是一个局部对象,"new JavaApplication()",值可能在下一个GC循环中被垃圾收集。

当用户点击链接时,它不保证执行回调方法exit

让您大桥对象在一个领域,以防止它的垃圾收集:

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版本可以*改变垃圾收集的时间和行为。你很幸运,但在后来的版本中,你的运气已经耗尽。

+0

因此'bridge'成为新的匿名ChangeListener实例的类属性。 ObservableValue'addListener'方法对新的ChangeListener实例持有强烈的引用,以防止它被垃圾收集,从而阻止了Bridge实例被垃圾收集? –

+1

那是关于它的,是的。 – VGR