Platform.runLater() and SwingUtilities.invokeLater()
Javafx
3
White

Nobody viết ngày 31/03/2022

Hi

Some developers whose native language is NOT English seem to be either bad in English or too lazy to read the APIs. However I have an impression that most of them are too lazy to read the APIs and to try to develop a little app, in order to study the described effects. The old man Confucius coined the term:

If you tell me something I'll forget it
If you show me something I'd remember it
if you let me do it I'll understand it

and the old man Laotzu philosophized:

He who can't read is pitiful
He who can read and believes unconditionally in the written is stupid
He who can read and distinguishes the right from the wrong is wise

Beside the basic lingual elements (if, while, for, etc.) Object Oriented Programming Language (OOPL) bases on a lot of language-specific objects such as Math, JFrame, ArrayList, HashMap, etc. Software Developers usually develop their own objects using the lingual elements, interface and class or from the implemention/extension of the provided standard interfaces/objects. BUT if they don't like to read the APIs and dislike to examine the APIs then how can they understand the APIs and consequently the OOP? And I just wonder how could this boy become an IT developer or could find a decent IT job?

The OOP fundamental is always OS and Computer independent. Namely: commands or instructions are processed sequentially, an object is itself an entity. Example: the sequence.

1   import javafx.application.*;
2   import javafx.scene.*;
3   import javafx.stage.*;
4   import javafx.fxml.*;
5   import javafx.scene.layout.*;
6   // Joe Nartca (c)
7   public class eMvcDict extends Application {
8         public void start(Stage stage) throws Exception {
9            stage.setTitle("Joe's eDict Anh-Viet Tu Dien");
10          FXMLLoader loader = new FXMLLoader(getClass().getResource("./fxmlFile.fxml"));
11          AnchorPane root = (AnchorPane)loader.load();
12          myController myCon = loader.<MyController>getController();
13          stage.setScene(new Scene(root));
14          stage.setOnCloseRequest(e -> myCon.exit());
15          stage.show();
16       }
17 }

if line 12 is placed before line 11 the result is a NullPointerException at line 14. Reason: FXMLLoader instantiates only object loader (line 10), and that's all. At Line 11 method load() starts to create a VIEW Object with all included instructions (Box, TextField, etc.) and the CONTROLLER (instruction fx:controller="MyController").

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
        at eMvcDict.lambda$start$0(eMvcDict.java:14)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        ...

The hint line 14 tells us that myCon is a null in the Lambda Expression. If the getRoot() is invoked prior load() the return value of getRoot() is also a null.

The basic lingual elements are the understanding of the relationship between the lingual elements. If A is not instantiated then B cannot inherit all characteristics of A. In simplest words: no load(), no CONTROLLER.

Because IT documents were written by the technical they are usually hard to comprehend (incl. my blog here). The only way to understand what the written means is to try with a little app that includes the APIs (that one wants to understand). The mentioned JavaFX developer wanted to know about the method

Platform.runLater(Runnable)

What does the Platform API say? It says

public static void runLater(Runnable runnable)
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.
NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.
This method must not be called before the FX runtime has been initialized.
...

Please pay more attention to the NOTE which is "applications should avoid flooding JavaFX with too many pending Runnables..."

  • less runLater() using the JFX threads (see the following example)
  • runLater() for long-running threads (see the modified example) which could be the responses from DB operations or similar.
  • too many superfluous runLater() could hang the app (unresponsive app).

You understand? Probably nada, huh? The question is: what is the so-called "any thread"? Let try with an example

public class JFX_RunLaterX extends Application  {
  @SuppressWarnings("unchecked")
  public void start(Stage stage) {
    ObservableList<People> list =
        FXCollections.observableArrayList(new People("101","Chuck", "San Diego"),
                                          new People("102","Bill", "Boston"),
                                          new People("103","Dick", "Houston"));
    table = new TableView<>();
    TableColumn<People, String> col_1 = new TableColumn<>("ID");
    col_1.setCellValueFactory(new PropertyValueFactory<>("ID"));

    TableColumn<People, String> col_2 = new TableColumn<>("Name");
    col_2.setCellValueFactory(new PropertyValueFactory<>("Name"));

    TableColumn<People, String> col_3 = new TableColumn<>("City");
    col_3.setCellValueFactory(new PropertyValueFactory<>("City"));

    table.getColumns().addAll(col_1, col_2, col_3);
    table.setItems(list);

    lab = new Label("Waiting for EVENT");
    Button start = new Button("ADD ROW");
    start.setOnAction(new EventHandler<ActionEvent>() {
       public void handle(ActionEvent e) {
        String id = getInput("ID");
        if (id == null) return;
        String name = getInput("Name");
        if (name == null) return;
        String city = getInput("City");
        if (city == null) return;
        lab.setText("Event received and inserted into TableView");
        table.getItems().add(new People(id, name, city));
      }
    });
    VBox vbox = new VBox( );
    vbox.setAlignment(Pos.CENTER);
    vbox.setPadding(new Insets(10, 5, 5, 5));
    vbox.getChildren().addAll(lab, table, start);
    //
    Scene scene = new Scene(vbox);
    stage.setResizable(false);
    stage.setScene(scene);
    stage.setX(10);
    stage.setY(10);
    stage.show();
  }
  //
  private String getInput(String msg) {
    Dialog<String> dialog = new TextInputDialog();
    dialog.setTitle("Input Dialog");
    dialog.setHeaderText(msg);
    Optional<String> rep = dialog.showAndWait();
    if (!rep.isPresent()) return null;
    return rep.get();
  }
  public void stop() {
    Platform.exit( );
    System.exit(0);
  }
  private Label lab;
  private TableView<People> table;
  //----------------------------------------------------------------------------
  public class People {
    public People(String id, String name, String city) {
      ID = new SimpleStringProperty(id);
      Name = new SimpleStringProperty(name);
      City = new SimpleStringProperty(city);
    }
    private SimpleStringProperty ID, Name, City;
    public String getID() { return ID.get(); }
    public String getName() { return Name.get(); }
    public String getCity() { return City.get(); }
    public void setID(String id) {ID.set(id);}
    public void setName(String name) {Name.set(name);}
    public void setCity(String city) {City.set(city);}
  }
}

The JFX thread created by JavaFX EventHandler or JFX Object Lambda Expression works perfectly

    start.setOnAction(new EventHandler<ActionEvent>() {
       public void handle(ActionEvent e) {
        ....
      }
    });
// or with Lambda Expression
    start.setOnAction((a -> {
        String id = getInput("ID");
        if (id == null) return;
        String name = getInput("Name");
        if (name == null) return;
        String city = getInput("City");
        if (city == null) return;
        lab.setText("Event received and inserted into TableView");
        table.getItems().add(new People(id, name, city));
    });

So, Platform.runLater() is here superfluous.
alt text alt text

BUT if an update comes from a NON-JavaFX thread then the update must be queued by Platform.runLater(). Let modify the above codes with an Interface for callback (Java_Event) and a Event-Listener (Java_EventListener).

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
import javafx.collections.*;
import javafx.beans.property.*;
import javafx.scene.control.cell.PropertyValueFactory;
//
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
// Joe Nartca (C)
public class JFX_RunLaterX extends Application implements Java_Event {
  @SuppressWarnings("unchecked")
  public void start(Stage stage) {
     // create an EventListener and register this app
    Java_EventListener je = new Java_EventListener();
    ForkJoinPool.commonPool().submit(je);
    je.register(this);
    // JFX
    ObservableList<People> list =
        FXCollections.observableArrayList(new People("101","Chuck", "San Diego"),
                                          new People("102","Bill", "Boston"),
                                          new People("103","Dick", "Houston"));
    table = new TableView<>();
    TableColumn<People, String> col_1 = new TableColumn<>("ID");
    col_1.setCellValueFactory(new PropertyValueFactory<>("ID"));

    TableColumn<People, String> col_2 = new TableColumn<>("Name");
    col_2.setCellValueFactory(new PropertyValueFactory<>("Name"));

    TableColumn<People, String> col_3 = new TableColumn<>("City");
    col_3.setCellValueFactory(new PropertyValueFactory<>("City"));

    table.getColumns().addAll(col_1, col_2, col_3);
    table.setItems(list);

    lab = new Label("Waiting for EVENT");
    Button start = new Button("SEND EVENT");
    /*
    start.setOnAction((a -> {
        String id = getInput("ID");
        if (id == null) return;
        String name = getInput("Name");
        if (name == null) return;
        String city = getInput("City");
        if (city == null) return;
        lab.setText("Event received and inserted into TableView");
        table.getItems().add(new People(id, name, city));
    });
    */
    start.setOnAction(a -> {
      String id = getInput("ID");
      if (id == null) return;
      String name = getInput("Name");
      if (name == null) return;
      String city = getInput("City");
      if (city == null) return;
      // set the Java_Event
      je.setEvent(id+"!"+name+"!"+city);
    });
    VBox vbox = new VBox( );
    vbox.setAlignment(Pos.CENTER);
    vbox.setPadding(new Insets(10, 5, 5, 5));
    vbox.getChildren().addAll(lab, table, start);
    //
    Scene scene = new Scene(vbox);
    stage.setResizable(false);
    stage.setScene(scene);
    stage.setX(10);
    stage.setY(10);
    stage.show();
  }
  //
  private String getInput(String msg) {
    Dialog<String> dialog = new TextInputDialog();
    dialog.setTitle("Input Dialog");
    dialog.setHeaderText(msg);
    Optional<String> rep = dialog.showAndWait();
    if (!rep.isPresent()) return null;
    return rep.get();
  }
  // Callback from Java_Event
  public void onEvent(String str) {
    String[] ev = str.split("!");
    //lab.setText("Event received and inserted into TableView");
    //table.getItems().add(new People(ev[0], ev[1], ev[2]));
    Platform.runLater(() -> {
      lab.setText("Event received and inserted into TableView");
      table.getItems().add(new People(ev[0], ev[1], ev[2]));
    });
  }
  public void stop() {
    Platform.exit( );
    System.exit(0);
  }
  private Label lab;
  private TableView<People> table;
  //----------------------------------------------------------------------------
  public class People {
    public People(String id, String name, String city) {
      ID = new SimpleStringProperty(id);
      Name = new SimpleStringProperty(name);
      City = new SimpleStringProperty(city);
    }
    private SimpleStringProperty ID, Name, City;
    public String getID() { return ID.get(); }
    public String getName() { return Name.get(); }
    public String getCity() { return City.get(); }
    public void setID(String id) {ID.set(id);}
    public void setName(String name) {Name.set(name);}
    public void setCity(String city) {City.set(city);}
  }
  class Java_EventListener implements Runnable {
    public Java_EventListener( ) { }
    public void register(Java_Event je) {
      if (!set.contains(je)) set.add(je);
    }
    public void setEvent(String str) {
      this.str = str;
    }
    public void run() {
      while (true) {
        if (str != null) {
          for (Java_Event je : set) je.onEvent(str); // callback
          str = null;
        }
        try {
          Thread.sleep(200);
        } catch (Exception ex) { }
      }
    }
    private volatile CopyOnWriteArraySet<Java_Event> set = new CopyOnWriteArraySet<Java_Event>( );
    private volatile String str = null;
  }
}
// the interface
interface Java_Event {
    public void onEvent(String str);
}

The Platform.runLater(Runnable) is used to queue a NON -JavaFX thread

  // Callback from Java_Event
  public void onEvent(String str) {
    String[] ev = str.split("!");
    //lab.setText("Event received and inserted into TableView");
    //table.getItems().add(new People(ev[0], ev[1], ev[2]));
    Platform.runLater(() -> {
      lab.setText("Event received and inserted into TableView");
      table.getItems().add(new People(ev[0], ev[1], ev[2]));
    });
  }

and the result is
alt text
alt text
alt text
alt text

If the block Platform.runLater() is commented Exception is risen:
alt text

SWING threads are more tolerant than JavaFX threads. If too many threads try to update a SWING component then it could happen that some updates could get lost. In this case SwingUtilities.invokeLater() must be applied.

import javax.swing.*;
import javax.swing.table.*;
import java.util.concurrent.*;
// Joe Nartca (C)
public class SWING_InvokeLater extends JFrame implements Java_Event {
  public SWING_InvokeLater(Java_EventListener je) {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    String data[][] = { {"101","Chuck","San Diego"},
                        {"102","Bill","Boston"},
                        {"103","Dick","Houston"}};
    String column[] = {"ID","NAME","CITY"};
    //
    table = new JTable(new DefaultTableModel(data, column));
    model = (DefaultTableModel) table.getModel();
    model.addTableModelListener(e -> {
      System.out.println("Table changed!");
    });
    JScrollPane sp = new JScrollPane(table);
    JButton start = new JButton("START EVENT");
    start.addActionListener(e -> {
      String id = JOptionPane.showInputDialog(this,"ID");
      if (id == null) return;
      String name = JOptionPane.showInputDialog(this,"Name");
      if (name == null) return;
      String city = JOptionPane.showInputDialog(this,"city");
      if (city == null) return;
      //setEvent for Callback
      je.setEvent(id+"!"+name+"!"+city);
    });
    lab = new JLabel("Waiting for EVENT");
    JPanel p1 = new JPanel(); p1.add(lab);
    JPanel p2 = new JPanel(); p2.add(sp);
    JPanel p3 = new JPanel(); p3.add(start);
    add("North", p1);
    add("Center",p2);
    add("South", p3);
    pack();
    setVisible(true);
    je.register(this);
  }
  // Callback from Java_Event
  public void onEvent(String str) {
    String[] ev = str.split("!");
    model.addRow(new Object[]{ev[0], ev[1], ev[2]});
    lab.setText("Event received and inserted into JTable");
    /*
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        model.addRow(new Object[]{ev[0], ev[1], ev[2]});
        lab.setText("Event received and inserted into JTable");
      }
    });
    */
  }
  private JLabel lab;
  private JTable table;
  private DefaultTableModel model;
  //
  public static void main(String... argv) throws Exception {
    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
    Java_EventListener je = new Java_EventListener();
    SWING_InvokeLater sil = new SWING_InvokeLater(je);
    ForkJoinPool.commonPool().submit(je);
  }
}
// Event Interface
interface Java_Event {
  public void onEvent(String str);
}
//
class Java_EventListener implements Runnable {
  public Java_EventListener( ) { }
  public void register(Java_Event je) {
    if (!set.contains(je)) set.add(je);
  }
  public void setEvent(String str) {
    this.str = str;
  }
  public void run() {
    while (true) {
      if (str != null) {
        for (Java_Event je : set) je.onEvent(str);
        str = null;
      }
      try {
        Thread.sleep(200);
      } catch (Exception ex) { }
    }
  }
  private volatile CopyOnWriteArraySet<Java_Event> set = new CopyOnWriteArraySet<Java_Event>( );
  private volatile String str = null;
}

alt text
Joe

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Nobody

20 bài viết.
560 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
1 2
Chao Cac Ban I was absent for a very long time... To my wonder that Kipalog is still alive. It's a very good news. Today I show you a brief tutor...
Nobody viết 5 tháng trước
1 2
White
1 0
(Ảnh) I found this question in a Vietnamese forum ((Link)). The questioner is certainly not a man who's studied Computer Science (or in Vietnamese...
Nobody viết 4 tháng trước
1 0
White
1 0
This tutorial is a summary of the two last tutorials 1. OCR: (Link) 2. JAVA: (Link) With the knowledge we have about the way how to process an Ima...
Nobody viết 3 tháng trước
1 0
Bài viết liên quan
White
0 0
Hi Again I found this question "(Link)" on a popular Vietnamese forum for "developers" of all kinds. The questioner has developed a MVC JavaFX app...
Nobody viết 2 tháng trước
0 0
White
2 1
Pomodoro là gì? Bạn đã từng nghe về nguyên tắc Pomodoro chưa? Có thể bạn đã từng nghe đâu đó về nó, đại loại thì nghe nó cũng gần gần kiểu như Pot...
Sang Nguyễn Đắc viết hơn 5 năm trước
2 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
20 bài viết.
560 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!