Синтаксический анализ JavaFX JSON из потока URL останавливает интерфейс - PullRequest
0 голосов
/ 28 августа 2018

У меня эта задача запущена в потоке. Проблема в том, что он замораживает интерфейс каждый раз, когда выполняется. Замораживание дольше, когда интернет работает медленно. Как я могу предотвратить зависание пользовательского интерфейса, даже если он все еще собирает данные с URL-адреса?

Task<Void> task = new Task<Void>(){
        @Override
        public Void call() throws Exception {
            while (true) {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run(){
                        String json = null;
                        try {
                            psname = null;
                            PumpSites n = table.getSelectionModel().getSelectedItem();
                            psname = n.getPs();
                            if(psname == "Cubacub")
                                json = readUrl(""); //read json from thingspeak.com webpage
                            else if(psname == "Canduman")
                                json = readUrl("");
                        } catch (InterruptedIOException iioe)
                        {
                            btn1.setTextFill(Color.RED);
                            btn2.setTextFill(Color.RED);
                            btn3.setTextFill(Color.RED);
                            btn4.setTextFill(Color.RED);
                            btn5.setTextFill(Color.RED);
                            btn1.setText("NULL");
                            btn2.setText("NULL");
                            btn3.setText("NULL");
                            btn4.setText("NULL");
                            btn5.setText("NULL");
                        }
                        catch (IOException ioe)
                        {
                            btn1.setTextFill(Color.RED);
                            btn2.setTextFill(Color.RED);
                            btn3.setTextFill(Color.RED);
                            btn4.setTextFill(Color.RED);
                            btn5.setTextFill(Color.RED);
                            btn1.setText("NULL");
                            btn2.setText("NULL");
                            btn3.setText("NULL");
                            btn4.setText("NULL");
                            btn5.setText("NULL");
                        }
                        catch (Exception e1) {
                            btn1.setTextFill(Color.RED);
                            btn2.setTextFill(Color.RED);
                            btn3.setTextFill(Color.RED);
                            btn4.setTextFill(Color.RED);
                            btn5.setTextFill(Color.RED);
                            btn1.setText("NULL");
                            btn2.setText("NULL");
                            btn3.setText("NULL");
                            btn4.setText("NULL");
                            btn5.setText("NULL");
                        } 

                        Gson gson = new Gson();        
                        Page page = gson.fromJson(json, Page.class);

                        for (Item item : page.feeds)
                        {
                            det2 = 1;
                            btn1.setText(item.field1);
                            btn2.setText(item.field2);
                            btn3.setText(item.field3);
                            btn4.setText(item.field4);
                            btn5.setText(item.field5);
                            f2 = Float.parseFloat(item.field2);
                            f3 = Float.parseFloat(item.field3);
                            //float f5 = Float.parseFloat(item.field5);
                            if (f2 <= 10.0)
                            {
                                btn1.setTextFill(Color.RED);
                                btn2.setTextFill(Color.RED);
                            }
                            else
                            {
                                btn1.setTextFill(Color.BLUE);
                                btn2.setTextFill(Color.BLUE);
                            }
                            if (f3 < 0.9 || f3 > 1.2)
                            {
                                btn3.setTextFill(Color.RED);
                            }
                            else
                            {
                                btn3.setTextFill(Color.BLUE);
                            }
                            /*if (f5 > 5.0)
                            {
                                btn5.setTextFill(Color.RED);
                            }
                            else
                            {
                                btn5.setTextFill(Color.BLUE);
                            }*/
                            btn4.setTextFill(Color.BLUE);
                        }   
                        if(det2 == 0)
                        {
                            btn1.setTextFill(Color.RED);
                            btn2.setTextFill(Color.RED);
                            btn3.setTextFill(Color.RED);
                            btn4.setTextFill(Color.RED);
                            btn5.setTextFill(Color.RED);
                            btn1.setText("NULL");
                            btn2.setText("NULL");
                            btn3.setText("NULL");
                            btn4.setText("NULL");
                            btn5.setText("NULL");
                        }
                        det2 = 0;

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

Проблема в том, что он останавливает интерфейс каждый раз, когда выполняется. Замораживание дольше, когда интернет работает медленно. Как я могу предотвратить зависание пользовательского интерфейса, даже если он все еще собирает данные из URL?

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Если вы используете фоновый поток, вызывайте Platform.runLater с длительным Runnable в качестве параметра, вы фактически ничего не добились. Runnable все еще выполняется в потоке приложения JavaFX, замораживающем ваше приложение.

Вместо этого вы должны собрать все данные в фоновом потоке и обработать их до точки, где вам просто нужно настроить некоторые свойства сцены. Затем вы используете Platform.runLater для выполнения этих обновлений.

Но хорошая новость заключается в том, что для этого сценария разработан класс, который может немного упростить ваш код: ScheduledService.

Просто убедитесь, что вы не обращаетесь к GUI каким-либо образом из фонового потока (ни для чтения, ни для настройки свойств).

В следующем примере упрощенный пример должен демонстрировать общий подход. Он вычисляет несколько кратных значения, выбранного с помощью Spinner в фоновом потоке с задержкой 10 секунд между каждым вычислением:

@Override
public void start(Stage primaryStage) {
    Spinner<Integer> spinner = new Spinner(1, 100, 1);

    // ensure the value is available in a way that allows synchronisation
    final AtomicReference<Integer> input = new AtomicReference<>(spinner.getValue());
    spinner.valueProperty().addListener((o, oldValue, newValue) -> input.set(newValue));

    final int outputCount = 10;

    GridPane root = new GridPane();
    root.add(spinner, 0, 0, 2, 1);

    // create output grid
    Text[] output = new Text[outputCount];
    for (int i = 1; i <= output.length; i++) {
        Text text = new Text(Integer.toString(spinner.getValue() * i));
        output[i - 1] = text;
        root.addRow(i, new Text("Value multiplied by " + i + " = "), text);
    }
    root.setPrefWidth(300);

    ScheduledService<int[]> service = new ScheduledService<int[]>() {

        @Override
        protected Task<int[]> createTask() {
            return new Task<int[]>() {

                @Override
                protected int[] call() throws Exception {
                    // retrieve value and set it to null to denote a change
                    // that was already handled to avoid doing unnecessary
                    // work
                    Integer value = input.getAndSet(null);

                    int[] result = null;

                    if (value != null) {
                        int valueAsInt = value;
                        result = new int[outputCount];
                        for (int i = 0; i < outputCount; i++) {
                            result[i] = (i + 1) * valueAsInt;
                        }
                    }

                    // simpulate delay
                    Thread.sleep(2000);

                    return result;
                }

            };
        }

    };

    service.valueProperty().addListener((o, oldValue, newValue) -> {
        // update GUI
        if (newValue != null) {
            for (int i = 0; i < outputCount; i++) {
                output[i].setText(Integer.toString(newValue[i]));
            }
        }
    });

    service.setPeriod(Duration.seconds(10));

    // make sure service uses a daemon thread
    service.setExecutor(Executors.newSingleThreadScheduledExecutor((Runnable r) -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }));

    service.start();

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

Я рекомендую просмотреть javadoc ScheduledService, чтобы ознакомиться с его возможностями. Он также позволяет реагировать на исключения в задаче и указывать стратегию отсрочки.

0 голосов
/ 28 августа 2018

Поток пользовательского интерфейса зависает, потому что вы все еще выполняете всю логику в потоке приложения JavaFX ( Platform.runLater ).

Вы должны сделать что-то вроде этого:

public Void call() throws Exception 
{
        while (true) 
        {
            try
            {   
                //get json 
            } catch(Exception e) 
            {   
                Platform.runLater(new Runnable()    
                {
                    @Override
                    public void run()
                    {
                        //set buttons color and text here
                    }   
                 }
             }
             //Rest of your logic here
         }
}

Идея состоит в том, что все, что собирается изменить компонент пользовательского интерфейса из отдельного потока, должно обрабатываться в Platform.runLater

...