当前位置: 首页 > 知识库问答 >
问题:

JavaFX WebEngine等待ajax完成

郝玄天
2023-03-14

我正在用JavaFX开发一个数据挖掘应用程序,它依赖于WebView(因此也依赖于WebEngine)。挖掘分两步进行:首先,用户使用UI导航到WebView中的一个网站,以配置可以在哪里搜索感兴趣的数据。其次,使用定期运行的后台任务,WebEngine加载相同的文档,并尝试从加载的文档中提取数据。

这种方法在大多数情况下都能很好地工作,但是最近我遇到了一些使用AJAX呈现内容的页面的麻烦。为了检查WebEngine是否加载了文档,我监听LoadWorkerStateProperty。如果状态转换为succesfull,我知道文档已加载(以及可能运行在document.ready()或等效文件上的任何javascript)。这是因为javascript是在JavaFX线程上执行的,如果我没有弄错的话(来源:https://blogs.oracle.com/JavaFX/entry/communication_between_javascript_and_javafx)。但是,如果启动了AJAX调用,javascript执行结束,引擎让我知道文档已经准备好了,尽管显然还没有准备好,因为内容仍然可能由于未完成的AJAX调用而发生变化。

有没有办法解决这个问题,注入一个钩子,以便在AJAX调用完成时通知我?我尝试在$.ajaxsetup()中安装一个默认的完整处理程序,但这是相当不可靠的,因为如果ajax调用覆盖完整处理程序,则不会调用默认处理程序。另外,我只能在文档首次加载之后(到那时,一些AJAX调用可能已经在运行)才可以注入。我已经用一个upcall测试了这个注入,它可以很好地适用于根据命令(在注入默认处理程序之后)启动的AJAX调用,这些调用不提供自己的完整处理程序。

我正在寻找两个东西:首先:一种挂接到AJAX调用的完成处理程序的通用方法,其次:一种等待WebEngine完成所有AJAX调用并在之后通知我的方法。

共有1个答案

越福
2023-03-14

我也遇到过这个问题,并通过提供我自己的sun.net.www.protocol.http.httpurlConnection实现来解决这个问题,我使用它来处理任何AJAX请求。我的类被方便地称为AjaxHttpurlConnection,它挂钩到getInputStream()函数中,但不返回它的原始输入流。相反,我将PipedInputStream的一个实例返回到WebEngine。然后读取来自原始输入流的所有数据,并将其传递到管道流。这样,我得到了2个好处:

  1. 我知道何时收到了最后一个字节,因此AJAX请求已被完全处理。
  2. 我甚至可以获取所有传入的数据并已经使用它(如果我愿意的话)。

首先,您必须告诉Java使用您的URLConnection实现,而不是默认的实现。为此,必须为其提供自己版本的URLStreamHandlerFactory。你可以在SO(例如这一个)或通过Google找到许多关于这个主题的线程。为了设置工厂实例,请在main方法的早期位置放置以下内容。这就是我的样子。

import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;

public class MyApplication extends Application {

    // ...

    public static void main(String[] args) {
        URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
            public URLStreamHandler createURLStreamHandler(String protocol) {
                if ("http".equals(protocol)) {
                    return new MyUrlConnectionHandler();    
                }
                return null; // Let the default handlers deal with whatever comes here (e.g. https, jar, ...)
            }
        });
        launch(args);
    }
}
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class MyUrlConnectionHandler extends Handler {

    @Override
    protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {

        if (url.toString().contains("ajax=1")) {
            return new AjaxHttpURLConnection(url, proxy, this);
        }

        // Return a default HttpURLConnection instance.
        return new HttpURLConnection(url, proxy);
    }
}

最后但并非最不重要的是,这里有AjaxHttpurlConnection

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.IOUtils;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class AjaxHttpURLConnection extends HttpURLConnection {

    private PipedInputStream pipedIn;
    private ReentrantLock lock;

    protected AjaxHttpURLConnection(URL url, Proxy proxy, Handler handler) {
        super(url, proxy, handler);
        this.pipedIn = null;
        this.lock = new ReentrantLock(true);
    }

    @Override
    public InputStream getInputStream() throws IOException {

        lock.lock();
        try {

            // Do we have to set up our own input stream?
            if (pipedIn == null) {

                PipedOutputStream pipedOut = new PipedOutputStream();
                pipedIn = new PipedInputStream(pipedOut);

                InputStream in = super.getInputStream();
                /*
                 * Careful here! for some reason, the getInputStream method seems
                 * to be calling itself (no idea why). Therefore, if we haven't set
                 * pipedIn before calling super.getInputStream(), we will run into
                 * a loop or into EOFExceptions!
                 */

                // TODO: timeout?
                new Thread(new Runnable() {
                    public void run() {
                        try {

                            // Pass the original data on to the browser.
                            byte[] data = IOUtils.toByteArray(in);
                            pipedOut.write(data);
                            pipedOut.flush();
                            pipedOut.close();

                            // Do something with the data? Decompress it if it was
                            // gzipped, for example.

                            // Signal that the browser has finished.

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } finally {
            lock.unlock();
        }
        return pipedIn;
    }
}
  • 如果您正在使用多个WebEngine对象,则可能很难判断哪个对象实际打开了URLConnection以及哪个浏览器已完成加载。
  • 您可能已经注意到我只处理http连接。我还没有测试我的方法在多大程度上可以转移到https等(这里不是专家:O)。
  • 如您所见,我知道何时实际使用我的AjaxHttpurlConnection的唯一方法是当对应的url包含Ajax=1时。对我来说,这就足够了。但是,由于我不太擅长html和http,所以我不知道webengine是否可以以任何不同的方式发出AJAX请求(例如,头字段?)。如果有疑问,您可以返回修改后的url连接的实例,但这当然意味着一些开销。
  • 如开头所述,一旦从输入流中检索到数据,您可以立即使用它(如果您希望这样做的话)。您可以以类似的方式获取WebEngine发送的请求数据。只需包装getOutputStream()函数,并放置另一个中间流来获取正在发送的内容,然后将其传递给原始输出流。
 类似资料:
  • 问题内容: 我正在使用Selenium 2 WebDriver来测试使用AJAX的UI。 有没有一种方法可以使驱动程序稍稍等待Ajax请求将完成。 基本上我有这个: 问题答案:

  • 问题内容: 嗨,我的脚本中有2个Ajax调用,我需要它们运行asnyc以节省时间,但是我需要第二个才能等待第一个完成。 有什么想法吗?谢谢 问题答案: 如果使用jQuery 1.5+,则可以使用jQuery 完成。诸如此类的东西(缩短了ajax的简洁性,只需像上面那样传递对象) 您不知道它们将以什么顺序返回,因此,如果您手动滚动此请求,则需要检查另一个请求的状态并等待它返回。

  • 问题内容: 我需要等到我所有的ajax函数都完成后,再继续执行。 我的特殊情况是,在提交表单之前,我需要翻译表单中的某些字段。我通过ajax调用将其转换为外部站点。根据表单中的某些值,我需要进行更多或更少的翻译。完成所有翻译后(如果有),我必须使用ajax验证表单,如果表单有效,则提交。 这是我的方法: 首先,我有一个函数发送ajax调用并对接收到的数据进行处理: 然后,当要提交表单时,我将执行以

  • 我通读了Dart/flatter中的Async/Await/then,试图理解为什么aysnc函数中的Await不会等到完成后再继续。在我的UI中,有一个按钮调用一个异步方法来返回一个位置,该位置总是返回null,并且不等待函数完成。 该函数将调用推送到一个新的UI页面,该页面选择一个位置,并应返回一个结果。如何使该函数等待结果?我不是在使用异步吗?

  • 问题内容: 为什么每当我将ajax放入for循环中时,它都无法很好地同步? 例如,我的代码是: 为什么它先调用Ajax查询?是否有可能让ajax查询在继续之前完成?因为它在完成填充之前就清除了数组。:/ 问题答案: 首先,您确实需要了解Ajax调用是如何异步的(这就是Ajax中的“ A”所代表的意思)。这意味着调用仅启动ajax调用(它将请求发送到服务器),其余代码愉快地继续运行。有时,在其余代码

  • 问题内容: 我有以下 我有一个运行一些ajax的函数,然后根据ajax是否成功返回true或false。我从代码的多个位置调用了这个ajax函数。 因为该函数在ajax完成之前结束,所以它总是返回false。如何避免这种情况? 我读到一些建议我在函数中执行的操作,然后将和函数移至我的和函数。但是,在我的方法中,我进行了大量的计算。很多代码。因此,问题在于,如果我将功能移至其他功能,那么我将复制一堆