【问题标题】:JavaFX wait for Webview fully load or cancel everything after 15 secondsJavaFX 等待 Webview 完全加载或在 15 秒后取消所有内容
【发布时间】:2021-09-22 19:45:43
【问题描述】:

我正在用 JavaFX 编写一个 nfcreader 程序。使用 ACR122U nfcreader 和 github 上的 nfcreader 类。它按预期工作,现在我将卡放在读卡器上,读取卡 uid 后,它会从我们的服务器加载网页并显示信息。

问题:

  • NFC 读卡器在后台任务中运行,如果读卡器上有卡,则始终触发。 我需要防止这种情况发生 - 在网页加载时。

我做了什么:

  • 如果 nfcread 服务成功,它会调用方法“loadZeitdatenToWebview(nfcuid)”
  • 在这个方法中,一个变量被设置为 true 来告诉 Java webview 仍然加载数据
  • 如果 webview 的 Workerstate 成功,我将此变量设置为 false,否则为 true... 15 秒内由 pausetransition 检查 - 如果我在 15 秒内没有获得 webview“成功”,它将取消整个 nfcread并告诉用户没有连接到 服务器 - 到目前为止
  • 如果我没有连接到服务器,它会正常工作,它会停止 nfcread 并且一切正常
  • 但如果我获得了 webview 成功 - 它只能工作一次......我必须等待 15 秒(从暂停转换开始),直到它再次读取并向我显示网页。 所以暂停过渡不是实现我需要的最佳方式:( - 但还有什么可以使用的? 简而言之:readnfc -> 从卡中获取 uid -> webview 调用页面 dep。在 uid 上 -> 在网页完全加载并显示给用户之前,不应该有其他 nfcreader -> 如果 15 秒后没有与服务器的连接,请停止一切​​...

nfcread 的代码成功...:

 nfcread.setOnSucceeded(new EventHandler<WorkerStateEvent>() { //if service succeeded

        @Override
        public void handle(WorkerStateEvent event) {
            labelReaderStatus.setText("NFC Readerstatus: warte auf NFC");
            labelReaderStatusInfo.setStyle("-fx-background-color: WHITE; -fx-text-fill: black;");
            labelReaderStatusInfo.setText("warte...");

            System.out.println("NFC UID Card: " + nfcread.getValue()); //return value from service nfcread
            if (nfcread.getValue() != "" && nfcread.getValue() != "Error" && nfcread.getValue() != null && nfcread.getValue() != "Error no Card") {
                uidRead = nfcread.getValue();

                labelReaderStatusInfo.setText("NFC Chip wurde gelesen - beginne mit Auswertung ");
                labelReaderStatusInfo.setStyle("-fx-background-color: GREEN; -fx-text-fill: white;");
                //chekc if webengine still loads data if not loadmethod
                //loadData to webview

                if (!waitForData) {
                loadZeitdatenToWebview(uidRead);
                } else {
                    System.out.println("Still loading data for "+uidRead);
                    nfcread.reset();

                }


            } //wenn uidRead keinen Fehler und nicht leer ist
            else if (nfcread.getValue() == "Error") {...

这里是等待 webview 或停止的代码...

//start webengine
private void loadZeitdatenToWebview(String uid) {


    //wait till page loaded:
    engine = webviewer.getEngine();
    engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {


        if (newState == Worker.State.SUCCEEDED) {
            System.out.println("Workerstate Succeeded");
            System.out.println("waitforData: " +waitForData );
            // new page has loaded, process:
            this.waitForData = false;
            zeiterfassungErfolgreich();

            System.out.println("waitforData done reset: " +waitForData );

                }
        if (engine.getLoadWorker().getState() != Worker.State.RUNNING) {
            System.out.println("Workerstate Running ");
            System.out.println("load state while data request:" + engine.getLoadWorker().getState()); //SCHEDULED
            this.waitForData = true;
            zeiterfassungRunning();
        }

    });

    engine.load(httpHostforZeiterfassung + uid);
    //engine.load("http://10.0.0.11/nfcread.php?uid=" + uid);
    System.out.println("here: " + engine.getLoadWorker().getState());
}


private void zeiterfassungErfolgreich() {
    System.out.println("zeiterfassungErfolgreich");
    System.out.println("Fertig:" +waitForData);
    labelReaderStatusInfo.setText("Zeiterfassung erfolgreich...");
    //nfcread.reset();
}

private void zeiterfassungRunning() {
    System.out.println("zeiterfassungRunning check - for data within 15 seconds");
    //engine.getLoadWorker().cancel();
    PauseTransition pause = new PauseTransition(Duration.seconds(15));

    if (engine.getLoadWorker().getState() != Worker.State.SUCCEEDED) pause.setOnFinished(event -> {
        if (engine.getLoadWorker().getState() == Worker.State.SUCCEEDED) {pause.stop(); waitForData=false; }
        else {
            engine.executeScript("window.stop()");
            waitForData = false;
            labelReaderStatusInfo.setStyle("-fx-background-color:red;  -fx-text-fill: white;");
            labelReaderStatusInfo.setText("Error keine Verbindung zum Server!");
            nfcread.cancel();
        }
        //System.out.println("Running:" +waitForData);
    });
    pause.play();
    System.out.println("here2: " + engine.getLoadWorker().getState());


}


//end webengine

如果我可以通过这种方式使用 pausetransition,如果 webview 成功或更好,我该如何防止等待时间?我应该使用什么来完成这项工作? 感谢@all 这里!!

附加信息: 如果我尝试在这 15 秒内创建一张卡片,我的实际代码会给我这个输出:

  • 等待卡片..
  • 关闭失败
  • 事件:0BDF8D69
  • 数据请求时的加载状态:已调度
  • zeiterfassung 运行检查 - 15 秒内的数据
  • 这里 2:预定
  • 这里:正在运行
  • 等待卡片..
  • zeiterfassungErfolgreich
  • Fertig:真
  • 数据请求时的加载状态:成功
  • zeiterfassung 运行检查 - 15 秒内的数据
  • 这里2:成功
  • 关闭失败
  • 事件:0BDF8D69
  • 仍在加载数据等待卡片。

【问题讨论】:

  • 我建议重复使用相同的PauseTransition,这样您就可以随意停止和重新启动它。您也可以取消waitForData 字段并简单地查询getEngine().getLoadWorker().isRunning() 方法。你需要小心你如何添加你的听众。每次调用 loadZeitdatenToWebview 方法时,您都会向工作人员添加一个新的侦听器,而无需删除旧的侦听器。您应该只添加一次侦听器,可能是在您第一次创建 WebView 时。
  • 当工人成功时你应该停止PauseTransition。话虽如此,即使加载成功,我也无法看到是什么迫使您等待 15 秒(特别是如果您重写代码以在完成后停止转换)。当加载成功时,您将waitForData 设置为false,所以我不确定是什么阻止您加载下一组数据。你能澄清一下吗?
  • 您的代码令人困惑。如果我理解正确,这就是我会做的。我将创建一个具有类范围的Worker.State 变量。我会调用loadZeitdatenToWebview(uidRead); 我会创建一个每秒运行的Timeline 并将循环计数设置为15。在Timeline 中,我会检查Worker.State 变量是否被引擎监听器更新。
  • 您在setOnFinished 处理程序中有对stop() 的调用,因此仅在动画完成时调用它(使对stop() 的调用毫无用处)。而且我知道如果加载成功,您不想等待 15 秒。我不明白的是您的 current 代码如何在加载成功时强制您等待 15 秒。
  • "如果webview.load 成功,我如何停止或取消Paustransition?" -> 检查WebEngine documentation 以了解加载成功时的操作。然后在适当的处理程序方法中对暂停转换调用stop()。注意:Sedrick 刚刚写了一个答案。

标签: javafx webview


【解决方案1】:

这是我认为你应该做的一个例子。

  1. 尝试加载引擎。
  2. 创建一个全局Worker.State 变量,用于跟上 stateProperty 侦听器的状态。使用 Timeline 每隔 15 秒检查一次 Worker.State 变量的值。
/*
Code found here -> http://tutorials.jenkov.com/javafx/webview.html
*/
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Duration;

public class App extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    Worker.State workerState;
    Timeline oneSecondWonder;
    
    public void start(Stage primaryStage) {
        primaryStage.setTitle("JavaFX WebView Example");

        WebView webView = new WebView();

        webView.getEngine().getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
            workerState = newState;
        });        
        webView.getEngine().load("http://google5.com");//Use to see what happens after 15 seconds.
        //webView.getEngine().load("http://google.com");//Use to see what happens before 15 seconds.
        
        oneSecondWonder = new Timeline(
                 new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
                     System.out.println("Worker State: " + workerState);
                     switch(workerState)
                     {
                         case FAILED:
                             System.out.println("FAILED");
                             break;
                         case SUCCEEDED:
                             System.out.println("SUCCEEDED");
                             oneSecondWonder.stop();
                             System.out.println("I am stopping the Timeline and moving forward with code here!");
                             oneSecondWonder.stop();
                             //Next set of code here!
                             break;
                         case CANCELLED:
                             //Code here if needed!
                             break;
                         //Other cases if needed here!
                     }
        }));
        oneSecondWonder.setOnFinished((t) -> {
            System.out.println("The Timeline ended. I am alerting the user about his/her next options");
        });        
        oneSecondWonder.setCycleCount(15);
        oneSecondWonder.play();
        
        VBox vBox = new VBox(webView);
        Scene scene = new Scene(vBox, 960, 600);

        primaryStage.setScene(scene);
        primaryStage.show();

    }
}

【讨论】:

  • 天哪,谢谢 Sedrick... !!!!我将实现它或更好地尝试在我的代码中以正确的方式理解和使用它!谢谢!!
  • 我个人认为这里使用Timeline 比它需要的更复杂,但这只是一个意见。 @loomy Here is a gist 我在问题 cmets 中暗示的内容。它只是一个骨架类,显示了我的意思的基础知识。您必须重写一些东西才能将其集成到您的应用程序中。注意:我最终可能会删除要点。
  • @Slaw 我建议将要点作为答案发布,而不是删除要点。该解决方案足够不同,并在回答问题的同时增加了足够的价值,这样做很好,可以在没有很多额外解释的情况下发布。
  • 嘿 Sedrick 我第一次尝试实施您的解决方案 - 到目前为止它一直在工作,但如果我过于频繁/快速地将卡轻敲到读卡器上,它就会卡住并停止正常工作 - 所以我停止工作在此之上并实施了 Slaws 解决方案,这是“开箱即用”的工作 - 如果我向读者强调,它仍然有效。但是我更喜欢您的解决方案,因为看到正在发生的事情,现在它比 Slaws 解决方案更容易理解 - 我现在会继续 - 并将在几天内重试实施您的解决方案。 :) 非常感谢您的帮助!
  • 抱歉,我的解决方案不适合您。我很高兴斯劳的回答做到了。感谢您提供啤酒。我非常乐意在力所能及的范围内免费提供帮助。
【解决方案2】:

您的代码中至少有几个问题可能会导致您的整体问题。

  1. 您不会在加载成功(或失败)时停止PauseTransition

    看起来有人尝试实现这一点,但您是在动画的 onFinished 处理程序中实现的。该处理程序在动画完成时调用,因此已经停止/停止。在该处理程序中调用stop() 基本上是无操作的。

    重用单个PauseTransition 实例并根据负载工作程序的运行状态(例如,通过观察工作程序的running 属性)简单地启动和停止它可能会更容易。你也可以使用worker的运行状态代替你自己的waitForData标志。

  2. 每次加载新网页时,您都会向工作人员的 state 属性添加一个新侦听器,但不会删除旧侦听器。

    这意味着侦听器内部的工作是重复的,其中重复的次数会随着时间的推移而增加。首次初始化所有内容时,您应该只添加一次侦听器。

在某些方面,我认为您的思维过于线性。通常 GUI 编程是事件驱动的,它本质上是异步的。

这是我在上面和问题 cmets 中暗示的一个骨架示例。您显然需要重写一些东西才能将其集成到您的应用程序中,但它应该可以帮助您了解正在发生的事情并希望找到解决方案。

import javafx.animation.PauseTransition;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebView;
import javafx.util.Duration;

public class Foo {

  private final String baseUrl;
  private final PauseTransition timeout;
  private final WebView webView;

  public Foo(String baseUrl) {
    this.baseUrl = baseUrl;
    timeout = new PauseTransition(Duration.seconds(15));
    webView = new WebView();

    Worker<?> worker = webView.getEngine().getLoadWorker();
    // observe 'running' property to start/stop timeout
    worker.runningProperty().addListener(this::loaderRunningChanged);
    // observe 'state' property to handle success/failure
    worker.stateProperty().addListener(this::loaderStateChanged);

    timeout.setOnFinished(
        e -> {
          worker.cancel(); // stop trying to load
          handleTimeout();
        });
  }

  public void loadData(String uid) {
    if (!webView.getEngine().getLoadWorker().isRunning()) {
      webView.getEngine().load(baseUrl + uid);
    }
  }

  private void loaderRunningChanged(
      ObservableValue<? extends Boolean> obs, boolean wasRunning, boolean isRunning) {
    if (isRunning) {
      System.out.println("Loading...");
      timeout.playFromStart(); // restart timeout countdown
    } else {
      timeout.stop(); // load completed, stop timeout countdown
    }
  }

  private void loaderStateChanged(
      ObservableValue<? extends State> obs, State oldState, State newState) {
    // switch expressions standardized in Java 14 (preview feature in Java 12 and 13)
    switch (newState) {
      case SUCCEEDED -> handleSuccess();
      case FAILED -> handleFailure();
    }
  }

  private void handleSuccess() {
    System.out.println("Load succeeded.");
    // ...
  }

  private void handleFailure() {
    System.out.println("Load failed.");
    // ...
  }

  private void handleTimeout() {
    System.out.println("Load timeout.");
    // ...
  }
}

【讨论】:

  • 嘿 Slaw - 你的回答对我来说非常有效。是的,你是对的,我认为并且仍然认为过于线性......我必须记住对每一个可能的事件......或事件组合做出反应。 :) - 并且必须了解更多关于事件处理程序及其初始化的信息。非常感谢您的帮助!!!
  • 告诉我一个地址,我可以寄钱给你买啤酒:) 或者你喜欢的任何东西!你们都拯救了我的一天!
猜你喜欢
  • 1970-01-01
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-03
  • 2015-11-11
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
相关资源
最近更新 更多