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

时间:2016-02-03 21:17:59

标签: java jquery javafx jquery-callback javafx-webengine

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

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

所以,我创建了三个类:

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

    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按预期运行并更改某些元素的文本(添加索引编号)。
    第二个 executeScript永远卡住并坚持使用GUI,实际上是整个JavaFX应用程序。

    我理解为什么会发生这种情况......
    executeScript方法调用WebEngine(通过 Platform.runLater )来执行jQuery迭代元素并调用Java函数(每次使用不同的参数)。

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

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

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

    有什么方法可以解决吗?
    为什么WebEngine仅限于在JavaFX应用程序下工作? 为什么它只能用一个线程?

1 个答案:

答案 0 :(得分:3)

避免&#34;死锁的一种可能方法&#34;您正在体验的是当您已经在GUI线程(JavaFX应用程序线程)中运行时,不会调用 Private Sub SelectClinic_Load(sender As Object, e As EventArgs) Handles MyBase.Load clNameDGV.DefaultCellStyle.Font = New Font("Calibri", 15, FontStyle.Regular, GraphicsUnit.Point) clNameDGV.RowHeadersVisible = False clNameDGV.ColumnHeadersVisible = False clNameDGV.ReadOnly = True clNameDGV.Location = New Point((Me.Width - clNameDGV.Width) / 2, (Me.Height - clNameDGV.Height) / 2) ChClinic() End Sub Private Sub ChClinic() conn = New SqlConnection("Server=(Local);Database=DrDB;user=Tarak;Trusted_Connection=True;") conn.Open() Dim comStr As String = "Select Count(*) from Clinicinfo" Dim comm As New SqlCommand(comStr, conn) Dim i As Integer = comm.ExecuteScalar 'CHECKING IF ANY CLINIC INFORMATION IS FOUND If i = 0 Then If MessageBox.Show("No clinic information found." + vbNewLine + "Will you like to create new clinic info right now ?", "No Clinic Info Found", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = vbYes Then Dim nf As New ClinicDetails nf.TopMost = True nf.ShowDialog(Me) Else Me.Close() End If End If 'LOADING CLINIC NAMES INTO DATAGRIDVIEW Dim comStr3 As String = "Select Count(*) from Login_Detail" Dim comm3 As New SqlCommand(comStr3, conn) Dim iCh As Integer = comm3.ExecuteScalar 'MsgBox(iCh) Dim comStr2 As String If iCh = 0 Then comStr2 = "Select Clinic_Name from Clinicinfo" Else comStr2 = "Select DISTINCT Clinic_Name from Clinicinfo Where Clinic_Status = 'Active'" End If Dim comm2 As New SqlCommand(comStr2, conn) Dim rd As SqlDataReader Dim dt As New DataTable rd = comm2.ExecuteReader dt.Load(rd) 'clNameDGV.Columns.Clear() clNameDGV.AllowUserToAddRows = False clNameDGV.AutoGenerateColumns = True clNameDGV.DataSource = dt clNameDGV.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells) clNameDGV.Refresh() clNameDGV.Width = clNameDGV.Columns(0).Width clNameDGV.Location = New Point((Me.Width - clNameDGV.Width) / 2, (Me.Height - clNameDGV.Height) / 2) rd.Close() conn.Close() End Sub 。类似的东西:

Platform.runLater