首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >垃圾收集后Javascript失败的JavaFx WebView回调

垃圾收集后Javascript失败的JavaFx WebView回调
EN

Stack Overflow用户
提问于 2017-01-27 21:17:37
回答 1查看 2.2K关注 0票数 10

我目前正在开发一个基于JavaFX的应用程序,在这个应用程序中,用户可以与世界地图上标记的位置进行交互。为此,我使用了一种类似于http://captaincasa.blogspot.de/2014/01/javafx-and-osm-openstreetmap.html (1)中描述的方法。

但是,我面临一个与使用WebEngine的setMember()方法注入嵌入式HTML页面的Javascript回调变量有关的难以调试的问题(另请参阅https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/js-javafx.htm (2)作为官方教程)。

在运行程序一段时间时,回调变量正在失去其不可预测的状态!为了演示这种行为,我开发了一个最小的工作/失败示例。我在Windows 10机器上使用jdk1.8.0_121 64位.

JavaFx应用程序看起来如下:

代码语言:javascript
复制
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());
        });
    }

}

HTML文件"page.html“看起来如下:

代码语言:javascript
复制
<!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>

可以通过单击javaApp按钮来观察回调变量"Send data to Java in endless loop"的状态。它将继续尝试通过javaApp.callback运行回调方法,后者在Java中生成一些日志消息。警报被用作一种额外的通信渠道来支持事物(似乎总是有效的,而且目前被用作工作,但事情不是这样的……)。

如果一切都按设想工作,每次日志记录都应该打印出与以下行类似的内容:

代码语言:javascript
复制
callback retrieved: Test
2017/01/27 21:26:11 alerted: javaApp=webviewtests.WebViewJsCallbackTest$JavaScriptBridge@51fac693(type=object), data=Test

但是,经过一段时间( 2-7分钟内的任何内容),将不再检索回调,而是只打印如下行的日志:

2017/01/27 21:32:01 alerted: javaApp=undefined(type=object), data=Test

打印变量现在给出的是'undefined',而不是实例路径。一个奇怪的观察是,javaApp的状态并不是真正的“未定义”。使用typeof返回objectjavaApp === undefined的计算结果为false。这与回调调用不会引发异常的事实相一致(否则,将打印以"caugt exception: "开头的警报)。

使用VisualVM显示,故障时间恰好与垃圾收集器被激活的时间相吻合。这可以通过观察堆内存消耗,从大约下降。60 GC到16 GC由于GC。

那里发生了什么事?您知道我如何进一步调试这个问题吗?我找不到任何相关的知虫..。

非常感谢你的建议!

PS:当包含Javascript代码以通过传单(Cf1)显示世界地图时,这个问题的重现速度要快得多。加载或移动地图的大部分时间,立即导致GC做它的工作。在调试这个原始问题时,我将问题追溯到这里提供的最小示例。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-28 09:17:46

我通过在Java中创建一个实例变量bridge来解决这个问题,该变量保存通过setMember()发送到Javascript的JavaScriptBridge实例。这样,就可以防止实例的。

相关代码片段:

代码语言:javascript
复制
public class JavaScriptBridge {
    public void callback(String data) {
        System.out.println("callback retrieved: " + data);
    }
}

private JavaScriptBridge bridge;

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

    bridge = new JavaScriptBridge();
    webEngine.getLoadWorker().stateProperty().addListener((observableValue, oldValue, newValue) -> {
        if (newValue == State.SUCCEEDED) {
            JSObject window = (JSObject) webEngine.executeScript("window");
            window.setMember("javaApp", bridge);
        }
    });

    webEngine.setOnAlert(event -> {
        System.out.println(DATE_FORMAT.format(new Date()) + " alerted: " + event.getData());
    });
}

尽管代码现在运行顺利(同时配合传单),我仍然对这种意想不到的行为感到恼火.

编辑:对这种行为的解释是从Java 9 (感谢@dsh为您的澄清意见!当时我正在使用Java 8,不幸的是,我手头没有这些信息.)

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41903154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档