gpt4 book ai didi

java - 使用 WebEngine 执行多个脚本(或依赖于另一个脚本的脚本)

转载 作者:行者123 更新时间:2023-11-30 08:05:12 25 4
gpt4 key购买 nike

我想使用(JavaFX 的)WebEngine 在网页上执行一些 JavaScript(和 jQuery)并处理结果(使用 Java 代码)。
我对自己执行某些脚本的回调函数有疑问。

为了尽可能简单地说明我的问题,我做了一个最小化的代码来显示不需要的结果(它只是一个更大项目的一部分)。

所以,我创建了三个类:

  1. Browser - 一个包装 WebEngine 并用作加载网页和执行脚本的浏览器的类。
  2. JQueryFunction - 一个类,其中列出了几个与 jQuery 回调函数一起使用的函数,并且可以通过多种方式实现。 (基本上它们应该是用户按照自己的方式实现的功能接口(interface)——但为了简单起见,我将其设为普通功能)。
  3. Test - 具有执行两个脚本的 main 方法的类。

浏览器

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

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

public class Browser extends Application {
private static WebEngine webEngine;
private static JSObject window;

@Override
public void start(Stage primaryStage) throws Exception {
WebView browser = new WebView();
webEngine = browser.getEngine();

webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> observable, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
window = (JSObject) webEngine.executeScript("window;");

// The following lines inject jQuery (for pages that don't use already)
webEngine.executeScript("var script = document.createElement(\"script\");");
webEngine.executeScript("script.src = \"http://code.jquery.com/jquery-1.12.0.min.js\";");
webEngine.executeScript("document.getElementsByTagName(\"body\")[0].appendChild(script);");
}
}
});

primaryStage.setScene(new Scene(browser));
primaryStage.show();
//Platform.setImplicitExit(false);
}

public static JSObject getWindow() {
return window;
}

public static Object executeScript(String script) throws InterruptedException, ExecutionException {
FutureTask<Object> task = new FutureTask<>(new Callable<Object>() {
@Override
public Object call() throws Exception {
return webEngine.executeScript(script);
}
});
Platform.runLater(task);
return task.get();
}

public static void load(String url) {
Platform.runLater(new Runnable() {
@Override
public void run() {
webEngine.load(url);
}
});
}

public static void start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Application.launch(Browser.class);
} catch (IllegalStateException e) {}
}
}).start();
}
}

JQueryFunction

import java.util.concurrent.ExecutionException;

public class JQueryFunction {
public String function1(int index, String text) {
return (index+1) + ": " + text;
}

public String function2(int index, String text) throws InterruptedException, ExecutionException {
return (String) Browser.executeScript("$($(\".BlueArrows:eq(2) li\").find(\"a:first\")[index]).text();");
}
}

测试

public class Test {
public static void main(String[] args) throws Exception {
Browser.start();
Thread.sleep(1000); // Only used here to simplify the code
Browser.load("https://docs.oracle.com/javase/tutorial/");
Thread.sleep(3500); // Only used here to simplify the code
Browser.getWindow().setMember("javaApp", new JQueryFunction());
Browser.executeScript("$(\".BlueArrows:eq(0) li\").find(\"a:first\").text(function(index, text) { return javaApp.function1(index, text); });");
Browser.executeScript("$(\".BlueArrows:eq(0) li\").find(\"a:first\").text(function(index, text) { return javaApp.function2(index, text); });");
}
}

当我运行测试时,第一个 executeScript 按预期运行并更改了一些元素的文本(前置索引号)。
但是 second executeScript 永远卡住并卡住 GUI,实际上整个 JavaFX 应用程序。

我明白为什么会这样......
executeScript 方法调用 WebEngine(通过 Platform.runLater)来执行 jQuery,该 jQuery 遍历元素并调用 Java 函数(具有不同的每次参数)。

  • 第一次执行(调用 function1)从 function1 获取返回的字符串并将其应用于元素的文本。 (完全符合预期)!

  • 第二次执行(调用function2)执行调用Java函数的jQuery函数,但是Java函数需要执行更多的JavaScript(或jQuery),但是WebEngine直到第一次执行才会执行完成了。
    但是第一次执行不会完成,因为它取决于第二次执行的结果。

WebEngine 的编程方式是他将仅在一个线程中(在 FX 线程内)一个任务接一个任务(串行)执行。

有什么办法可以解决吗?
为什么WebEngine被限制只能在JavaFX应用下工作?
为什么它必须只与一个线程一起工作?

最佳答案

避免遇到“死锁”的一种可能方法是当您已经在 GUI 线程(JavaFX 应用程序线程)中运行时不调用 Platform.runLater。像这样的东西:

public static Object executeScript(String script) throws InterruptedException, ExecutionException {
if(Platform.isFxApplicationThread()) {
return webEngine.executeScript(script);
}
FutureTask<Object> task = new FutureTask<>(new Callable<Object>() {
@Override
public Object call() throws Exception {
return webEngine.executeScript(script);
}
});
Platform.runLater(task);
return task.get();

}

关于java - 使用 WebEngine 执行多个脚本(或依赖于另一个脚本的脚本),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35187909/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com