multithreading android子线程更新ui - 从JavaFX中的不同线程更新UI



android非ui线程修改ui (3)

执行此操作的最佳方法是在JavaFx中使用Task 。 到目前为止,这是我在JavaFx中更新UI控件时遇到的最好的技术。

Task task = new Task<Void>() {
    @Override public Void run() {
        static final int max = 1000000;
        for (int i=1; i<=max; i++) {
            updateProgress(i, max);
        }
        return null;
    }
};
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();

我正在开发一个具有多个TextField对象的应用程序,这些对象需要更新以反映相关后端属性的更改。 TextField不可编辑,只有后端可能会更改其内容。

据我所知,正确的方法是在单独的线程上运行繁重的计算,以免阻塞UI。 我使用javafx.concurrent.Task做了这个,并使用updateMessage()将单个值传回给JavaFX线程,这很有效。 但是,我需要更新多个值,因为后端会进行其处理。

由于后端值存储为JavaFX属性,我尝试将它们简单地绑定到每个GUI元素的textProperty ,并让绑定完成工作。 但是,这不起作用; 运行一会儿后,即使后端任务仍在运行, TextField也会停止更新。 没有例外。

我也尝试使用Platform.runLater()来主动更新TextField而不是绑定。 这里的问题是runLater()任务的调度速度比平台可以运行的速度快,因此GUI变得迟钝,即使在后端任务完成后也需要时间“赶上”。

我在这里找到了一些问题:

转换为UI的记录器条目不再随时间更新

JavaFX中的多线程挂起了UI

但我的问题仍然存在。

总结:我有一个后端对属性进行更改,我希望这些更改出现在GUI上。 后端是遗传算法,因此其操作被分解为离散的世代。 我想要的是TextField在代之间至少刷新一次,即使这会延迟下一代。 更重要的是,GUI响应良好,而GA运行速度快。

如果我没有明确问题,我可以发布一些代码示例。

UPDATE

我按照James_D的建议设法做到了。 为了解决后端必须等待控制台打印的问题,我实现了一个缓冲的控制台。 它存储要在StringBuffer打印的字符串,并在调用flush()方法时将它们实际附加到TextArea 。 我使用AtomicBoolean来防止下一代发生,直到刷新完成,因为它是由Platform.runLater() runnable完成的。 另请注意,此解决方案非常慢。


不确定我是否完全理解,但我认为这可能会有所帮助。

使用Platform.runLater(...)是一种合适的方法。

避免泛滥FX应用程序线程的技巧是使用原子变量来存储您感兴趣的值。在Platform.runLater(...)方法中,检索它并将其设置为sentinel值。 在后台线程中,更新Atomic变量,但只有在它被设置回其sentinel值时才发出新的Platform.runLater(...)。

我通过查看Task源代码来解决这个问题 。 看看如何实现updateMessage(..)方法(编写本文时的第1131行)。

这是一个使用相同技术的例子。 这只是一个(繁忙的)后台线程,它尽可能快地计算,更新IntegerProperty。 观察者观察该属性并使用新值更新AtomicInteger。 如果AtomicInteger的当前值为-1,则会调度Platform.runLater()。

在Platform.runLater中,我检索AtomicInteger的值并使用它来更新Label,在过程中将值设置回-1。 这表示我已准备好进行另一次UI更新。

import java.text.NumberFormat;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class ConcurrentModel extends Application {

  @Override
  public void start(Stage primaryStage) {

    final AtomicInteger count = new AtomicInteger(-1);

    final AnchorPane root = new AnchorPane();
    final Label label = new Label();
    final Model model = new Model();
    final NumberFormat formatter = NumberFormat.getIntegerInstance();
    formatter.setGroupingUsed(true);
    model.intProperty().addListener(new ChangeListener<Number>() {
      @Override
      public void changed(final ObservableValue<? extends Number> observable,
          final Number oldValue, final Number newValue) {
        if (count.getAndSet(newValue.intValue()) == -1) {
          Platform.runLater(new Runnable() {
            @Override
            public void run() {
              long value = count.getAndSet(-1);
              label.setText(formatter.format(value));
            }
          });          
        }

      }
    });
    final Button startButton = new Button("Start");
    startButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(ActionEvent event) {
        model.start();
      }
    });

    AnchorPane.setTopAnchor(label, 10.0);
    AnchorPane.setLeftAnchor(label, 10.0);
    AnchorPane.setBottomAnchor(startButton, 10.0);
    AnchorPane.setLeftAnchor(startButton, 10.0);
    root.getChildren().addAll(label, startButton);

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

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

  public class Model extends Thread {
    private IntegerProperty intProperty;

    public Model() {
      intProperty = new SimpleIntegerProperty(this, "int", 0);
      setDaemon(true);
    }

    public int getInt() {
      return intProperty.get();
    }

    public IntegerProperty intProperty() {
      return intProperty;
    }

    @Override
    public void run() {
      while (true) {
        intProperty.set(intProperty.get() + 1);
      }
    }
  }
}

如果你真的想从UI“驱动”后端:这会限制后端实现的速度,所以你看到所有的更新,考虑使用AnimationTimerAnimationTimer有一个handle(...) ,每帧渲染一次。 因此,您可以阻止后端实现(例如,通过使用阻塞队列),并在每次调用handle方法时释放一次。 在FX应用程序线程上调用handle(...)方法。

handle(...)方法接受一个时间戳(以纳秒为单位)的参数,因此如果每帧一次太快,您可以使用它来进一步减慢更新速度。

例如:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {

        final BlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(1);

        TextArea console = new TextArea();

        Button startButton = new Button("Start");
        startButton.setOnAction(event -> {
            MessageProducer producer = new MessageProducer(messageQueue);
            Thread t = new Thread(producer);
            t.setDaemon(true);
            t.start();
        });

        final LongProperty lastUpdate = new SimpleLongProperty();

        final long minUpdateInterval = 0 ; // nanoseconds. Set to higher number to slow output.

        AnimationTimer timer = new AnimationTimer() {

            @Override
            public void handle(long now) {
                if (now - lastUpdate.get() > minUpdateInterval) {
                    final String message = messageQueue.poll();
                    if (message != null) {
                        console.appendText("\n" + message);
                    }
                    lastUpdate.set(now);
                }
            }

        };

        timer.start();

        HBox controls = new HBox(5, startButton);
        controls.setPadding(new Insets(10));
        controls.setAlignment(Pos.CENTER);

        BorderPane root = new BorderPane(console, null, null, controls, null);
        Scene scene = new Scene(root,600,400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static class MessageProducer implements Runnable {
        private final BlockingQueue<String> messageQueue ;

        public MessageProducer(BlockingQueue<String> messageQueue) {
            this.messageQueue = messageQueue ;
        }

        @Override
        public void run() {
            long messageCount = 0 ;
            try {
                while (true) {
                    final String message = "Message " + (++messageCount);
                    messageQueue.put(message);
                }
            } catch (InterruptedException exc) {
                System.out.println("Message producer interrupted: exiting.");
            }
        }
    }

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

Java中的访问修饰符。

Java访问修饰符用于在Java中提供访问控制。

1.默认:

只能访问同一个包中的类。

例如,

// Saved in file A.java
package pack;

class A{
  void msg(){System.out.println("Hello");}
}

// Saved in file B.java
package mypack;
import pack.*;

class B{
  public static void main(String args[]){
   A obj = new A(); // Compile Time Error
   obj.msg(); // Compile Time Error
  }
}

此访问比公共访问受到更多限制并受到保护,但受限于私有。

2.公开

可以从任何地方访问。 (全球访问)

例如,

// Saved in file A.java

package pack;
public class A{
  public void msg(){System.out.println("Hello");}
}

// Saved in file B.java

package mypack;
import pack.*;

class B{
  public static void main(String args[]){
    A obj = new A();
    obj.msg();
  }
}

输出:你好

3.私人

只能在同一个类中访问。

如果您尝试访问另一个类中的私有成员将导致编译错误。 例如,

class A{
  private int data = 40;
  private void msg(){System.out.println("Hello java");}
}

public class Simple{
  public static void main(String args[]){
    A obj = new A();
    System.out.println(obj.data); // Compile Time Error
    obj.msg(); // Compile Time Error
  }
}

4.受保护

只能访问同一个包和子类中的类

例如,

// Saved in file A.java
package pack;
public class A{
  protected void msg(){System.out.println("Hello");}
}

// Saved in file B.java
package mypack;
import pack.*;

class B extends A{
  public static void main(String args[]){
    B obj = new B();
    obj.msg();
  }
}

输出:你好





java multithreading user-interface javafx-2