JavaFX 在继续之前等待页面加载
JavaFX Wait for Page to Load Before Continuing
我有一个为 WebView 的 WebEngine 设置新网页的方法,需要等到页面加载完成才能继续当前方法。
基本上我想要:
MyMethod()
{
navigateToNewUrl();
waitForPageToLoad(); // This is the part I am struggling with
doSomethingElse();
}
我试过使用 ChangeListener(),但它只会在我的方法执行完毕后执行。我用谷歌搜索了许多导致更多挫败感的术语,例如 "java non-blocking wait for boolean"。最终我做到了启动新线程(以防止应用程序 GUI 锁定)和使用 CountDownTimer(而不是 Thread.join() )。下面的代码是我无数次尝试得到我想要的东西。
public static Boolean pageLoaded;
public volatile static CountDownLatch latch;
private static void TestMethod()
{
// Set the CountDownLatch
latch = new CountDownLatch(1);
// Set the page loaded flag
pageLoaded = false;
// Navigate to new window
myWebView.getEngine().load("https://www.google.com/");
// Call a method, that starts a thread, that checks if the page has loaded
WaitForPageLoaded();
// Wait for the CountDownLatch, latch
try { latch.await(); } catch (InterruptedException e) { /* TODO Auto-generated catch block */ e.printStackTrace(); }
// Write result to console
System.out.println("[pageLoaded] " + pageLoaded);
}
private static void WaitForPageLoaded()
{
Thread thread = new Thread()
{
public void run()
{
// 120 iterations * 250 ms = 30 seconds
for (int i = 0; i < 120; i++)
{
// Check if page has loaded
if (pageLoaded == true)
{
break;
}
else
{
System.out.println("Waited " + (i*250) + " milliseconds, so far...");
try { Thread.sleep(250); }
catch (InterruptedException e) { /* TODO Auto-generated catch block */ e.printStackTrace(); }
}
}
// Tell the calling method to move on with life
latch.countDown();
}
}
}
在另一个 .java 文件中 (class) 我有:
// Get the WebEngine and set a listener for a state change
WebEngine webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener
(
new ChangeListener<State>()
{
public void changed(@SuppressWarnings("rawtypes") ObservableValue ov, State oldState, State newState)
{
if (newState == State.SUCCEEDED)
{
// Set the page loaded flag
OtherClassName.pageLoaded = true;
}
}
}
);
与几乎所有 UI 框架一样,JavaFX 基于事件驱动模型。在幕后有一个循环运行(在 FX 应用程序线程中)来处理事件。你不应该直接处理这个循环。
您正在尝试通过重新创建此循环并等待条件来解决问题,而不是注册响应适当事件的处理程序。无需在线程(哪个线程?没有明显的方法来执行此操作)中等待页面加载,您只需响应每个页面完成加载并执行测试过程中的下一步。您可以通过在 Web 引擎的 stateProperty
.
上注册一个侦听器来完成此操作
所以你的结构应该是这样的
public class MyApp extends Application {
private WebView webView ;
private WebEngine engine ;
// variables representing the current state of your testing process...
@Override
public void start(Stage primaryStage) {
webView = new WebView();
engine = webView.getEngine();
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
// new page has loaded, process:
testMethod();
}
});
// set up UI etc...
engine.load(...); // load first page
}
private void testMethod() {
// check current state, do next appropriate thing, update variables...
}
}
此结构完全实现了您正在尝试执行的操作 - testMethod()
被重复调用,但仅在每个页面完成加载时调用 - 但 "waiting" 由框架为您管理。
我有一个为 WebView 的 WebEngine 设置新网页的方法,需要等到页面加载完成才能继续当前方法。
基本上我想要:
MyMethod()
{
navigateToNewUrl();
waitForPageToLoad(); // This is the part I am struggling with
doSomethingElse();
}
我试过使用 ChangeListener(),但它只会在我的方法执行完毕后执行。我用谷歌搜索了许多导致更多挫败感的术语,例如 "java non-blocking wait for boolean"。最终我做到了启动新线程(以防止应用程序 GUI 锁定)和使用 CountDownTimer(而不是 Thread.join() )。下面的代码是我无数次尝试得到我想要的东西。
public static Boolean pageLoaded;
public volatile static CountDownLatch latch;
private static void TestMethod()
{
// Set the CountDownLatch
latch = new CountDownLatch(1);
// Set the page loaded flag
pageLoaded = false;
// Navigate to new window
myWebView.getEngine().load("https://www.google.com/");
// Call a method, that starts a thread, that checks if the page has loaded
WaitForPageLoaded();
// Wait for the CountDownLatch, latch
try { latch.await(); } catch (InterruptedException e) { /* TODO Auto-generated catch block */ e.printStackTrace(); }
// Write result to console
System.out.println("[pageLoaded] " + pageLoaded);
}
private static void WaitForPageLoaded()
{
Thread thread = new Thread()
{
public void run()
{
// 120 iterations * 250 ms = 30 seconds
for (int i = 0; i < 120; i++)
{
// Check if page has loaded
if (pageLoaded == true)
{
break;
}
else
{
System.out.println("Waited " + (i*250) + " milliseconds, so far...");
try { Thread.sleep(250); }
catch (InterruptedException e) { /* TODO Auto-generated catch block */ e.printStackTrace(); }
}
}
// Tell the calling method to move on with life
latch.countDown();
}
}
}
在另一个 .java 文件中 (class) 我有:
// Get the WebEngine and set a listener for a state change
WebEngine webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener
(
new ChangeListener<State>()
{
public void changed(@SuppressWarnings("rawtypes") ObservableValue ov, State oldState, State newState)
{
if (newState == State.SUCCEEDED)
{
// Set the page loaded flag
OtherClassName.pageLoaded = true;
}
}
}
);
与几乎所有 UI 框架一样,JavaFX 基于事件驱动模型。在幕后有一个循环运行(在 FX 应用程序线程中)来处理事件。你不应该直接处理这个循环。
您正在尝试通过重新创建此循环并等待条件来解决问题,而不是注册响应适当事件的处理程序。无需在线程(哪个线程?没有明显的方法来执行此操作)中等待页面加载,您只需响应每个页面完成加载并执行测试过程中的下一步。您可以通过在 Web 引擎的 stateProperty
.
所以你的结构应该是这样的
public class MyApp extends Application {
private WebView webView ;
private WebEngine engine ;
// variables representing the current state of your testing process...
@Override
public void start(Stage primaryStage) {
webView = new WebView();
engine = webView.getEngine();
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
// new page has loaded, process:
testMethod();
}
});
// set up UI etc...
engine.load(...); // load first page
}
private void testMethod() {
// check current state, do next appropriate thing, update variables...
}
}
此结构完全实现了您正在尝试执行的操作 - testMethod()
被重复调用,但仅在每个页面完成加载时调用 - 但 "waiting" 由框架为您管理。