如何从JavaFX中的单独线程更新状态和进度

时间:2013-12-15 13:02:13

标签: multithreading textarea javafx-2 output

我需要能够调用一个单独的线程。线程分析文件并从中提取统计信息。

分析文件最多可能需要2分钟,在分析过程中,数据会打印到日志中。

我希望在前端有一个需要打印出分析的TextArea(分析时),我还希望有一个进度条来指示进度。所有这些都是在单独的线程中确定的。

我所做的是在UI类中创建一个方法,将字符串添加到Text Area,并将此类的引用传递给已启动的线程。

我的主要课程

package trymutilthread;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TryMutilThread extends Application {

    TextArea ta;

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

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        btn.setText("Start");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                startScheduledExecutorService();
            }
        });

        ta = new TextArea();        

        VBox vBox = new VBox();
        vBox.getChildren().addAll(btn, ta);

        StackPane root = new StackPane();
        root.getChildren().add(vBox);

        Scene scene = new Scene(root, 300, 750);

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

    private void startScheduledExecutorService() {
        final TryMutilThread classI = this;

        Task<Void> task = new Task<Void>() {
            @Override protected Void call() throws Exception {
                try {
                    ta.appendText("Starting Thread\n");
                    new SomeProcess(classI).doTheLogic();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        };

        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();      
    }

    public void appendText(String string) {        
        ta.appendText(string);
    }
}   

在线程中执行的类

package trymutilthread;

public class SomeProcess {

    TryMutilThread taClass = null;

    public SomeProcess (TryMutilThread taClass) {
        this.taClass = taClass;
    }

    public void doTheLogic() throws Exception{        
        taClass.appendText("Staring Thread");        

        for (int i = 0; i < 5000; i++) {
            taClass.appendText(String.valueOf(i));
        }

        taClass.appendText("Ending Thread");
    }
}

现在当我执行它时,它仍然只在线程结束后才将文本输出到TextArea。

我确实看过以下两篇文章:

JavaFX update textArea

Java client / server thread null pointer exception when quickly communicating messages

在流程结束之前,我无法将数据打印到日志中。


我更新了我的代码以创建一个任务。 但是现在我执行时会出现以下错误

Executing com.javafx.main.Main from F:\DEV\Projects\TryMutilThread\dist\run404234128\TryMutilThread.jar using platform C:\Program Files\Java\jdk1.7.0_10/bin/java
java.lang.NullPointerException
    at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.computeLinePadding(NGTextHelper.java:405)
    at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.access$200(NGTextHelper.java:292)
    at com.sun.javafx.sg.prism.NGTextHelper.buildTextLines(NGTextHelper.java:2357)
    at com.sun.javafx.sg.prism.NGTextHelper.validateText(NGTextHelper.java:1847)
    at com.sun.javafx.sg.prism.NGTextHelper.getCaretShape(NGTextHelper.java:1435)
    at javafx.scene.text.Text.getDecorationShapes(Text.java:1150)
    at javafx.scene.text.Text.impl_geomChanged(Text.java:757)
    at javafx.scene.text.Text$1.invalidated(Text.java:214)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:127)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
    at javafx.scene.text.Text.setText(Text.java:188)
    at com.sun.javafx.scene.control.skin.TextAreaSkin$17.invalidated(TextAreaSkin.java:610)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:359)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1034)
    at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1038)
    at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:978)
    at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:950)
    at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:119)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.scene.control.TextArea$TextAreaContent.insert(TextArea.java:196)
    at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:373)
    at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:308)
    at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:298)
    at trymutilthread.TryMutilThread.appendText(TryMutilThread.java:80)
    at trymutilthread.SomeProcess.doTheLogic(SomeProcess.java:26)
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:66)
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:62)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.lang.Thread.run(Thread.java:722)

然后我想了解错误,似乎我需要将交互代码放在Platform.runlater()中。

Java client / server thread null pointer exception when quickly communicating messages

我改变了类来执行线程

package trymutilthread;

import javafx.application.Platform;

public class SomeProcess {

    TryMutilThread taClass = null;

    public SomeProcess(TryMutilThread taClass) {
        this.taClass = taClass;
    }

    public void doTheLogic() throws Exception {
        taClass.appendText("Staring Thread");

        for (int i = 0; i < 5000; i++) {
            //remove this append line
            //taClass.appendText(i + "\n");                
            //And replaced it with platform.runlater
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    taClass.appendText("AGREED" + "\n");
                }
            });
        }

        taClass.appendText("Ending Thread");
    }
}

它执行没有任何错误,但现在它似乎又回到了开始...... UI被冻结,直到所有被添加到TextArea

1 个答案:

答案 0 :(得分:3)

问题在于你的FX应用程序线程泛滥得太多了。您的Platform.runLater(...)调用之间没有实际工作。这个问题可能会因你的实际应用程序而不是这个测试而消失,但为了模仿实际的长时间运行工作,你可以在那里放一个Thread.sleep(...):

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TextAreaBackgroundUpdateExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        final BorderPane root = new BorderPane();
        final TextArea textArea = new TextArea();
        final ProgressBar progress = new ProgressBar();
        final Button startButton = new Button("Start");

        final int maxCount = 5000 ;

        startButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Task<Void> task = new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        for (int i = 1; i <= maxCount; i++) {
                            Thread.sleep(10);
                            final int count = i ;
                            Platform.runLater(new Runnable() {
                                @Override
                                public void run() {
                                    textArea.appendText("Processed part " + count + " (of "+maxCount+")\n");                                    
                                }
                            });
                            updateProgress(i, maxCount);
                        }
                        return null;
                    }
                };
                progress.progressProperty().bind(task.progressProperty());
                Thread t = new Thread(task);
                t.setDaemon(true);
                t.start();
            }
        });

        root.setCenter(textArea);
        root.setTop(progress);
        root.setBottom(startButton);

        final Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


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