长时间运行并阻止javafx任务进度条更新 [英] Long running and blocking javafx task progress bar update

查看:207
本文介绍了长时间运行并阻止javafx任务进度条更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以长时间运行的任务,它正在阻塞。我必须等待此任务完成才能在GUI上显示结果。

我知道这项任务需要多长时间,所以我想通过进度条通知用户,剩下多少时间。

互联网上的所有例子都是这样的:

I have a task that can be long running, and it's blocking. I must wait for this task to complete to display results on the GUI.
I know how long this task will take, so I want to notify user with a progress bar, how much time is left.
All the examples on the internet are like this:

Task<Integer> task = new Task<Integer>() {
    @Override
    protected Integer call() throws Exception {
         int iterations;
         for (iterations = 0; iterations < 1000; iterations++) {
             updateProgress(iterations, 1000);

             // Now block the thread for a short time, but be sure
             // to check the interrupted exception for cancellation!
             try {
                 Thread.sleep(100);
             } catch (InterruptedException interrupted) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     break;
                 }
             }
         }
         return iterations;
     }
 };



此代码在循环中阻塞,并用<$更新它的进度c $ c> updateProgress 方法。

我的情况如下:


This code blocks in a loop, and update it's progress with updateProgress method.
My case would look like:

Task<Integer> task = new Task<Integer>() {
    @Override
    protected Integer call() throws Exception {
             try {
                 //This is my long running task.
                 Thread.sleep(10000);
             } catch (InterruptedException interrupted) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     return null;
                 }
             }
         }
         return iterations;
     }
 };



这在循环中不起作用,所以我不能用 updateProgress 更新进度。

一个解决方案是创建另一个运行我的阻塞任务的任务,并计算进度,但这似乎不雅。

有更好的方法吗?


This doesn't work in a loop, so I can't update progress with updateProgress.
One solution to this would be to create another task that would run my blocking task, and would count progress, but this seems inelegant.
Is there a better way to do this?

推荐答案

我发现很难看到一个用例其中任务阻止,但你知道它需要多长时间(至少有一个用动画API )。但假设您有一个,我会这样做:

I find it hard to see a use case in which the task blocks but you somehow know how long it would take (at least one that wouldn't be easier implemented with the animation API). But assuming you have one, I would approach it like this:

您的任务是阻塞的,因此必须在其他地方执行进度更新。基本上,您需要一个定期更新进度的后台线程。每次将帧渲染到屏幕时,执行此操作的最佳时间为: AnimationTimer class 就是为此而设计的。因此,您希望任务在开始运行时启动动画计时器,更新句柄(...)方法中的进度,并确保它在任务时停止停止运行。这看起来像:

Your task is blocking, so updates to the progress must be performed elsewhere. Basically, you would want a background thread that periodically updates the progress. The optimal time to do this would be once every time a frame is rendered to the screen: the AnimationTimer class is designed exactly for this. So, you want the task to start an animation timer when it starts running, update the progress in the handle(...) method, and make sure it stops when the task stops running. This looks like:

public abstract class FixedDurationTask<V> extends Task<V> {

    private final Duration anticipatedRuntime ;
    private AnimationTimer timer ;

    public FixedDurationTask(Duration anticipatedRuntime) {
        this.anticipatedRuntime = anticipatedRuntime ;
    }

    public Duration getAnticipatedRuntime() {
        return anticipatedRuntime ;
    }

    @Override
    protected void running() {
        timer = new AnimationTimer() {
            long startTime = System.nanoTime() ;
            long endTime = (long) (startTime + anticipatedRuntime.toMillis() * 1_000_000) ;
            long duration = endTime - startTime ;

            @Override
            public void handle(long now) {
                if (isRunning()) {
                    updateProgress(now - startTime, duration);
                } else {
                    stop();
                    if (getState() == State.SUCCEEDED) {
                        updateProgress(duration, duration);
                    }
                }
            }
        };
        timer.start();
    }
}

这是一个最小的测试(包括上面的类为了方便)。在文本字段中键入时间(以秒为单位),然后按 Enter

Here is a minimal test (with the above class included for convenience). Type a time in seconds in the text field and press Enter.

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class FixedDurationTaskTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        TextField durationField = new TextField();
        durationField.setPromptText("Enter time in seconds");
        Service<Void> service = new Service<Void>() {

            @Override
            protected Task<Void> createTask() {
                double duration = Double.parseDouble(durationField.getText());
                return new FixedDurationTask<Void>(Duration.seconds(duration)) {
                    @Override
                    public Void call() throws Exception {
                        Thread.sleep((long) getAnticipatedRuntime().toMillis());
                        return null ;
                    }
                };
            }

        };

        ProgressBar progress = new ProgressBar();
        progress.progressProperty().bind(service.progressProperty());

        durationField.disableProperty().bind(service.runningProperty());

        durationField.setOnAction(e -> service.restart());

        VBox root = new VBox(10, durationField, progress);
        root.setPadding(new Insets(20));
        root.setMinHeight(60);
        root.setAlignment(Pos.CENTER);

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static abstract class FixedDurationTask<V> extends Task<V> {

        private final Duration anticipatedRuntime ;
        private AnimationTimer timer ;

        public FixedDurationTask(Duration anticipatedRuntime) {
            this.anticipatedRuntime = anticipatedRuntime ;
        }

        public Duration getAnticipatedRuntime() {
            return anticipatedRuntime ;
        }

        @Override
        protected void running() {
            timer = new AnimationTimer() {
                long startTime = System.nanoTime() ;
                long endTime = (long) (startTime + anticipatedRuntime.toMillis() * 1_000_000) ;
                long duration = endTime - startTime ;

                @Override
                public void handle(long now) {
                    if (isRunning()) {
                        updateProgress(now - startTime, duration);
                    } else {
                        stop();
                        if (getState() == State.SUCCEEDED) {
                            updateProgress(duration, duration);
                        }
                    }
                }
            };
            timer.start();
        }
    }

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

这篇关于长时间运行并阻止javafx任务进度条更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆