【问题标题】:JavaFX Thread freezeJavaFX 线程冻结
【发布时间】:2015-12-04 13:03:31
【问题描述】:

我目前正在开发一个 JavaFX 项目。在 GUI 初始化时,我想使用 Selenium 和 FirefoxDriver 从 HTML 文档中读取一些信息。通常我会使用爬虫来获取信息,但是这个文档充满了 JavaScript,所以我只能使用 Selenium 来获取信息(我知道,这真的很糟糕)。

现在我遇到了这个过程最多需要 15 秒的问题,我想在 JavaFX 进度条上显示 Selenium 的进度。因此,我设置了一个线程来完成所有工作并尝试更新 GUI,但线程冻结,直到 Selenium 完成。

这是我的尝试:

public class SeleniumThread extends Thread
{
    private MainViewController main;

    @Override
    public void run()
    {
        try
        {
            WebDriver driver = new FirefoxDriver();
            driver.get("http://---.jsp");
            main.getMain().getPrimaryStage().toFront();
            main.getPbStart().setProgress(0.1);
            WebElement query = driver.findElement(By.id("user"));
            query.sendKeys(new String[] {"Username"});
            query = driver.findElement(By.id("passwd"));
            query.sendKeys(new String[] {"Password"});
            query.submit();
            driver.get("http://---.jsp");
            main.getPbStart().setProgress(0.2);
            sleep(1000);
            main.getPbStart().setProgress(0.25);
            driver.get("http://---.jsp");
            main.getPbStart().setProgress(0.4);
            sleep(1000);
            main.getPbStart().setProgress(0.45);
            driver.get("---.jsp");
            main.getPbStart().setProgress(0.6);
            sleep(1000);
            main.getPbStart().setProgress(0.65);
            query = driver.findElement(By.cssSelector("button.xyz"));
            query.click();
            sleep(1000);
            main.getPbStart().setProgress(0.85);
            System.out.println(driver.getPageSource());
            driver.quit();
        }
        catch(InterruptedException e)
        {
            // Exception ...
        }

    }

    public MainViewController getMain()
    {
        return main;
    }

    public void setMain(MainViewController main)
    {
        this.main = main;
    }
}

主视图控制器

public void startup()
{
    if(main.getCc().getMjUsername() != null &&
            main.getCc().getMjPassword() != null &&
            main.getCc().getMjUsername().length() != 0 &&
            main.getCc().getMjPassword().length() != 0)
    {
        SeleniumThread st = new SeleniumThread();
        st.setMain(this);
        st.setDaemon(true);
        st.run();
    }
}

我读过我应该使用像 Task 这样的 Worker,但我不知道如何实现它。而且我需要给这个Task传一个参数,因为我需要把我的primaryStage设置在最前面并更新进度条。

我希望你能理解我的问题。我将不胜感激。

【问题讨论】:

  • 1) 您看起来正试图直接从后台线程中进行 JavaFX 调用,虽然我对 JavaFX 知之甚少,但我知道这是不允许的,必须进行 JavaFX 调用JavaFX 应用程序线程。见Concurrincy in JavaFX。 2) 作为旁注,您似乎正在将 Thread 扩展到您真正想要实现 Runnable 的地方。
  • “我读到我应该使用像 Task 这样的 Worker,但我不知道如何实现它。” Task API 文档有很多示例,包括采用参数和更新进度的示例。

标签: java multithreading selenium javafx


【解决方案1】:

您正在尝试从不同的线程更新 UI。 UI 只能从 UI 线程更新。为此,请包装调用以更新进度:

Platform.runLater(() -> {main.getPbStart().setProgress(0.65);});

这会将 UI 的更新推送到 UI 线程中。

【讨论】:

  • 谢谢,这很好用。但这是不好的做法吗?我只是好奇,因为正如你所见,我对 Runnable 和 JavaFX 的工作并不多。
  • Platform.runLater 没问题,只要您不在其中发送数以万计的电话垃圾邮件 - 实际上不好的做法是从其他类访问您的进度条。通常,您会在 FXML 控制器类(而不是 main!!)中公开一个 writable property,并让您的线程在该属性上设置值,这些值从您的控制器类中反映到进度条上。这就是所谓的“数据封装”。
【解决方案2】:
  1. 您似乎试图直接从后台线程中进行 JavaFX 调用,虽然我对 JavaFX 知之甚少,但我知道这是不允许的,JavaFX 调用必须在 JavaFX 应用程序线程上进行。见Concurrency in JavaFX
  2. 您甚至没有创建后台线程。您调用 st.run(); ,它在调用线程上运行 st ——这不是您想要的。你应该打电话给st.start()
  3. 附带说明一下,您似乎正在将 Thread 扩展到您真正想要实现 Runnable 的地方。因此你真的应该打电话给new Thread(myRunnable).start();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 2020-05-25
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    相关资源
    最近更新 更多