限制 javafx gui 更新

Posted

技术标签:

【中文标题】限制 javafx gui 更新【英文标题】:Throttling javafx gui updates 【发布时间】:2014-06-22 16:30:36 【问题描述】:

我在随机时间以高频率接收数据对象,并且需要使用这些更新 JavaFX GUI。但是,我不想用大量可运行对象填充 javafx 事件队列(我使用 Platform.RunLater)。

我一直在思考如何最好地实现节流算法。

最好有一个单独的 GUIUpdater 线程来检查新对象的阻塞队列,然后休眠 30 毫秒,然后在无限循环中再次检查?在那种情况下,阻塞队列会是最优的数据结构吗?请注意,我只需要最新的数据对象,blockingQueue 是一个 FIFO 队列,我似乎不能只选择最新的条目。 或者 - 如果 nanoTime-startTime > 30 毫秒,只需使用 Platform.RunLater 更新 GUI 会更好吗?在这种情况下,我不需要单独的线程来执行 Platform.RunLater 调用。但是 - 如果在 30 毫秒内收到更新,然后一段时间内没有收到更新,则最后一次更新不会显示在 GUI 中。

关于如何为 JavaFX Platform.RunLater GUI 更新设计节流算法有什么建议吗?

【问题讨论】:

【参考方案1】:

这是Task 类中用于实现updateMessage(...) 方法和其他类似方法的惯用语。它提供了一个很好的、强大的解决方案来避免 FX 应用程序线程泛滥:

import java.util.concurrent.atomic.AtomicLong;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ThrottlingCounter extends Application 

    @Override
    public void start(Stage primaryStage) 
        final AtomicLong counter = new AtomicLong(-1);
        final Label label = new Label();
        final Thread countThread = new Thread(new Runnable() 
            @Override
            public void run() 
                long count = 0 ;
                while (true) 
                    count++ ;
                    if (counter.getAndSet(count) == -1) 
                        updateUI(counter, label);
                    
                
            
        );
        countThread.setDaemon(true);
        countThread.start();

        VBox root = new VBox();
        root.getChildren().add(label);
        root.setPadding(new Insets(5));
        root.setAlignment(Pos.CENTER);

        Scene scene = new Scene(root, 150, 100);
        primaryStage.setScene(scene);
        primaryStage.show();
    

    private void updateUI(final AtomicLong counter,
            final Label label) 
        Platform.runLater(new Runnable() 
            @Override
            public void run() 
                final String msg = String.format("Count: %,d", counter.getAndSet(-1));
                label.setText(msg);
            
        );
    

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

AtomicLong 保存用于更新标签的当前值。计数不断增加并更新AtomicLong,但仅在当前值为-1 时安排对Platform.runLater(...) 的调用。 Platform.runLater(...) 使用 AtomicLong 的当前值更新 Label 并将 AtomicLong 翻转回 -1,表示它已准备好进行新的更新。

这里的效果是在 FX 应用程序线程准备好处理它们时安排对Platform.runLater(...) 的新调用。没有可能需要调整的硬编码时间间隔。

【讨论】:

为什么在 platform.runlater-runnable 中设置标签后,AtomicLong 没有设置为“-1”(表示 gui 已准备好更新)? @user3607022 您需要检索当前值并将该值设置为-1原子地。如果您检索了要显示的值,然后在显示后将其设置为-1,则其他线程可能会在这两者之间再次设置该值:即1. UI线程检索该值,2.后台线程更新自值为!=-1 没有计划更新,3. ui 将值设置为-1。在这种情况下,您可能会“错过”该中间值;如果这是线程做的最后一件事,你会永远错过它。 有没有一种简单的方法可以准确地描述哪些更新被丢弃了? @user3607022 不:没有真正的理由关心。您正在专门更新 UI 属性;如果 UI 没有时间显示它,那么如果它没有收到也没关系:最终用户不会注意到。 我明白了。因此,换句话说,被删除的更新是 UI 没有时间显示的更新。我想我必须考虑一下这个实现是如何产生这个结果的。非常感谢您的帮助。

以上是关于限制 javafx gui 更新的主要内容,如果未能解决你的问题,请参考以下文章

Atitit 桌面软件跨平台gui解决方案 javafx webview

JavaFX 2:背景和 Platform.runLater 与任务/服务

线程限制/swingworkers

java - 如何在java fx中每2秒更新一次标签框?

JavaFX本地应用自动更新功能的实现FXLauncher

在 JavaFX 中更新 ListView 时出现 IndexOutOfBoundsException