将JRE版本1.8.0_66迁移到1.8.0_111后,我遇到了从JavaScript到JavaFX的调用问题。
长话短说:虽然有一个正在运行的后台线程,但WebView / WebEngine拒绝进行JS-to-Java调用。
我使用WebView呈现从域数据模型(DM)生成的HTML内容。内容包含分配了处理程序的元素,如下所示:
<a href='#' onclick='explainHeadWord(this)'>some_word</a>
JS部分看起来像:
function explainHeadWord(hwElement) {
jsBridge.jsHandleQuery(hwElement.innerHTML);
}
function testBridge() {
jsBridge.jsTest();
}
其中jsBridge
是Controller的内部Java类
public class JSBridge {
public void jsHandleQuery(String headWord) {
log("jsBridge: jsHandleQuery: requested %s", headWord);
handleQuery(headWord);
}
public void jsTest() {
log("jsBridge: jsTest: test succeeded ");
}
}
注入如下:
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
engine.setJavaScriptEnabled(true);
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("jsBridge", new JSBridge());
//engine.executeScript("jsTest()");
//engine.executeScript("explainHeadWord(document.getElementsByTagName('a')[0])");
//engine.executeScript("jsBridge.jsHandleQuery(document.getElementsByTagName('a')[0])");
}
除了主DM之外,我还有一个交叉引用索引,它是从DM构建的Map<String, Collection<String>>
,以及每次DM改变时在后台重建索引的触发器方法。第一种方法(在1.8.0_66版本上运行良好)基于ExecutorService:
private ExecutorService executor = Executors.newCachedThreadPool();
private Future<Boolean> indexer = executor.submit(() -> false);
...
private void rebuildIndex() {
executor.submit(() -> {
indexer.cancel(true);
indexer = executor.submit(() -> {
fullSearchIndex = getIndex();
if (isIndexingAborted()) return false;
return true;
});
try {
if (indexer.get()) {
log("resetIndex: Done");
updateTableView();
}
} catch (InterruptedException e) {
...
}
});
}
正如所料,单击WebView中的锚点导致JS调用jsBridge.jsHandleQuery(hwElement.innerHTML)
并最终在Controller中实现handleQuery(headWord)
方法调用。但是在将JRE迁移到版本1.8.0_111之后,WebView停止响应锚点击。
我调查了日志,发现注入jsBridge
成功,并执行了行window.setMember()
下面的代码中注释的测试脚本。单击<a>
元素导致什么都没有。但是,如果没有运行测试脚本(注释),日志中会出现记录:
<record>
<date>2017-01-09T02:00:47</date>
<millis>1483920047169</millis>
<sequence>160</sequence>
<logger>com.sun.webkit.WebPage</logger>
<level>FINE</level>
<class>com.sun.webkit.WebPage</class>
<method>fwkAddMessageToConsole</method>
<thread>11</thread>
<message>fwkAddMessageToConsole(): message = TypeError: jsBridge.jsHandleQuery is not a function. (In 'jsBridge.jsHandleQuery(hwElement.innerHTML)', 'jsBridge.jsHandleQuery' is undefined), lineNumber = 26, sourceId = jar:file:/.../jar.jar!/view.js</message>
</record>
片刻之后,后台(索引)线程完成,WebView中的内容重新加载,点击<a>
元素开始再次响应 - jsBridge.jsHandleQuery
被执行。
索引线程执行遍历DM的getIndex()
方法,并将收集的数据返回到DM中的Map
数据。没有与FX应用程序线程的任何交互,也没有WebView依赖于索引。替换fullSearchIndex = getMockIndex();
private Map<String, Collection<String>> getMockIndex() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) { }
return Collections.emptyMap();
}
后台线程中的不会改变<a>
的行为。
下一步是通过利用将后台线程重构为FX风格
javafx.concurrent.Service
但结果是一样的。
我会欣赏指出我做错了什么以及如何解决这个问题。
答案 0 :(得分:0)
尝试在侦听器之外实例化Bridge,即
final JSBridge bridge = new JSBridge();
engine.setJavaScriptEnabled(true);
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("jsBridge", bridge);
}
});
(为我工作从1.8.0_91迁移到1.8.0_121)。