【问题标题】:JavaFX: Service and GUIJavaFX:服务和 GUI
【发布时间】:2023-03-21 13:30:01
【问题描述】:

对于一个学校项目,我正在为 PLC 编写一个小可视化。因此,我有一个包含所有信息的 mySQL 数据库。目前,当我单击按钮时,程序会连接到数据库并在 ArrayList 中获取信息。然后它检查 ArrayList 中的信息并将 Data 放入 ListView 中。

问题是,我想让程序在服务中执行此操作。正如我所说,GUI 依赖于 ArrayList。而且我无法更改服务中的 GUI,因为随后会出现此异常

  Sep 02, 2016 9:19:02 PM javafx.concurrent.Service lambda$static$488
WARNING: Uncaught throwable in javafx concurrent thread pool
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
    at javafx.scene.Parent$2.onProposedChange(Unknown Source)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(Unknown Source)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(Unknown Source)
    at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(Unknown Source)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(Unknown Source)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(Unknown Source)
    at javafx.beans.value.WeakChangeListener.changed(Unknown Source)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(Unknown Source)
    at javafx.beans.property.StringPropertyBase.markInvalid(Unknown Source)
    at javafx.beans.property.StringPropertyBase.set(Unknown Source)
    at javafx.beans.property.StringPropertyBase.set(Unknown Source)
    at javafx.beans.property.StringProperty.setValue(Unknown Source)
    at javafx.scene.control.Labeled.setText(Unknown Source)
    at application.Controller$1$1.call(Controller.java:290)
    at application.Controller$1$1.call(Controller.java:1)
    at javafx.concurrent.Task$TaskCallable.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at javafx.concurrent.Service.lambda$null$493(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at javafx.concurrent.Service.lambda$executeTask$494(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

我的第一个想法是只提取服务中的 SQL 内容,然后通过方法获取数据

service.getValue();

但是这样做的问题是我不知道服务何时完成获取数据,因为目前,数据为空。

【问题讨论】:

  • 当多个线程试图修改同一个列表时会发生此异常。

标签: java multithreading javafx java-8


【解决方案1】:

基本上,你想运行一些进程并返回一些值, 当然Service 钉它。我就是这样处理我的案子的,而且效果很好。

Service process = new Service() {
    @Override
    protected Task createTask() {
        return new Task() {
            @Override
            protected ObjectX call() throws Exception {

                updateMessage("Some message that may change with execution");
                updateProgress( workDone, totalWork ); 
                return ObjectX;
            }
        };
    }
};

process.setOnSucceeded( e -> {

    ObjectX processValue = (ObjectX)process.getValue();

    // TODO, . . . 
    // You can modify any GUI element from here...
    // ...with the values you got from the service
});

process.start();

注意事项

  1. 内部方法protected ObjectX call()可以返回任何类型的对象。只要确保它是一个对象而不是原始类型。您可以在此过程中填充一些 GUI 元素并将其作为对象返回,例如。 protected VBox call() 。 . . . return my_vbox;
  2. ObjectX processValue = (ObjectX)processList.getValue(); => 你应该将你从Service 得到的值转换回你想要使用的对象。 如果只是Object,您可能不必这样做。但我怀疑你是否只需要使用Object
  3. 另见processList.setOnFailed()processList.setOnRunning()processList.setOnCancelled()processList.setOnScheduled()
  4. 您还可以像这样将一些 GUI 元素绑定到一些 Thread 属性

    label.textProperty.bind( process.messageProperty ); // messageProperty is a StringProperty
    progressBar.progressProperty.bind( process.progressProperty )
    
  5. 在调用process.start()之前,确保已经创建并启动了所有进一步增强流程的方法;在您开始该过程之前,什么都不会发生。

希望对你有帮助

【讨论】:

  • 感谢您的帮助,帮助很大
  • 是否有一种方法可以从另一个类更新进度,因为我在一个外部类中完成所有 SQL 工作,并希望从那里更新。
  • 我认为你不应该。我试过了,真的很乱。你可以考虑这样做。 . .执行 sql stuff 的外部类应该只返回一些状态(服务将其传递给其内部方法updateValue(Object)),然后您可以使用您获得的状态从ObjectX processValue = (ObjectX)processList.getValue(); 更新你的用户界面
【解决方案2】:

Service 类提供了一些方法,以便您知道它是否被取消、成功或失败

在扩展服务的类的构造函数中使用这些方法:

        // succeeded?
        this.setOnSucceeded(s -> {
            // ...
        });

        // failed
        this.setOnFailed(f -> {
            // ...
        });

        // cancelled?
        this.setOnCancelled(c -> {
            // ...
        });

还要记得使用updateProgress(current,maximum);所以你知道它在执行过程中发生了什么。

这是一个完整的例子:

import javafx.concurrent.Service;
import javafx.concurrent.Task;

public class ExampleService extends Service<Boolean> {

    /**
     * Constructor
     */
    public ExampleService() {

        // succeeded?
        this.setOnSucceeded(s -> {
            // ...
        });

        // failed
        this.setOnFailed(f -> {
            // ...
        });

        // cancelled?
        this.setOnCancelled(c -> {
            // ...
        });
    }

    @Override
    protected Task<Boolean> createTask() {
        return new Task<Boolean>(){

            @Override
            protected Boolean call() throws Exception {

                boolean result = false;


                //your code
                //.......
                //updateProgress(current,total)

                return result;
            }

        };
    }

}

【讨论】:

  • @Torben :) 很高兴听到
猜你喜欢
  • 2012-10-03
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 2013-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
相关资源
最近更新 更多