0

I am having two problems when trying to use "updateMessage" in a JavaFX task.

Issue #1 seems to be a known behavior, but I am not yet sure how exactly I can workaround it.This one is not (yet) critical to me. The problem is that not all the updates I am performing in a background Task are displayed in the UI (at least the UI does not hang/freezes anymore, which was my initial issue).

My Code of the UI part:

TextArea console = new TextArea();
        Button button01 = new Button("Start");
        button01.setOnAction(new EventHandler() {

            @Override
            public void handle(Event event) {

                if (ms.getState() == State.READY) {
                    ms.messageProperty().addListener(new ChangeListener<String>() {
                        @Override
                        public void changed(ObservableValue<? extends String> observable,
                                String oldValue, String newValue) {

                            console.appendText(newValue+"\n");
                        }
                    });
                    ms.start();
                }
            }

        });

My Service:

public class MyService extends Service<Object> {

    @Override
    protected Task createTask() {
        //here we use "MyTask" first to show problem #1
        MyTask ct = new MyTask();
        //here we use "MyTask2" first to show problem #2
//      MyTask2 ct = new MyTask2();
        try {
            ct.call();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("MyService end");
        return ct;
    }
}

My Task (#1)

public class MyTask extends Task<Object> {

    @Override
    public EventHandler<WorkerStateEvent> call() {
        System.out.println("call() is called");
        if (Thread.currentThread().getName().equals("JavaFX Application Thread")){//yes, this might not be right, but if I do not do this, my stuff is executed twice because "call()" is called twice, but the textarea area is just updated in the second run (the non javafx application thread).
            return null;
            } else{
            //actually here I want to do some 'heavy' stuff in the background
            //and many things of this heavy stuff should be displayed / logged within the UI
            //but very likely (hopefully) new messages (updateMessage) will not be send as fast as in the following loop

            for (int i=0;i<10000000;i++){
                updateMessage("This is update number'"+i+"' from the background thread");
            }

            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    try{
                        //here is the chance to get back to the view

                    }finally{
                    }
                }
            });

            return null;
        }
    }

This basically works, but not every single loop is displayed in the UI. How do I (correctly) make sure every loop is displayed? Screenshot: Messages are displayed but not for every loop

Issue #2 Currently blocks my attempt to bring my little text-based game into a JavaFX application. The main problem is that I am able to call "updateMessage" from the Task directly (see above), but not from a another (sub-)class which I would need to bring all message updates from my game (each message describes the progress of the game) to the UI.

The Task I use (Task #2):

public class MyTask2 extends Task<Object> {

    @Override
    public EventHandler<WorkerStateEvent> call() {
        // ...
        UITools myTools = new UITools();
        myTools.logToUITest("Just one simple message");
        // ...
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                try{
                    //here is the chance to get back to the view
                }finally{
                }
            }
        });

        return null;
    }

and the (sub-)class that I want to use to do the updateMessage (actually in my little game there would be even more classes that are called during the game and almost all of them trigger an update/message).

public class UITools  {
    public void logToUITest(String message){
        updateMessage(message);
        //how to allow 'updateMessage' from the Task to be executed from here?
    }

This already results in "The method updateMessage(String) is undefined...". How could I make it possible to call the updateMessage outside of the Task itself?

Stubbi
  • 1
  • 1
  • `createTask` shouldn't execute the task, it should just create it... – fabian Aug 22 '16 at 11:05
  • Issue #1 is a duplicate of http://stackoverflow.com/questions/31408363/javafx-changelistener-not-always-working. I don't really understand what issue #2 is asking. – James_D Aug 22 '16 at 14:35
  • @Fabian sounds logical and I understand that is seems to be wrong, but how to correct it ? @ James_D The problem of #2 is that I cannot execute the "updateMessage" outside of my Task. In this case my method "logToUiTest" does not allow to have the call "updateMessage", means I get a compilation warning/error in that line. – Stubbi Aug 23 '16 at 13:52
  • `protected Task createTask() { return new MyTask(); }` – fabian Aug 23 '16 at 13:57

1 Answers1

1

updateMessage() can only be called from within the call() method of a Task. It's a constraint imposed by the design of the Task class.

The missed message updates are due to the fact that there are too many updates and not all of them are forwarded to the event queue. Try to reduce the number of updates or sleep for a little while to separate them out in time

Paul Roub
  • 36,322
  • 27
  • 84
  • 93