【问题标题】:Handle on launched JavaFX Application处理已启动的 JavaFX 应用程序
【发布时间】:2015-05-19 20:06:49
【问题描述】:

如何获取使用以下代码启动的 JavaFX 应用程序的句柄?

CPUUsageChart.launch(CPUUsageChart.class);

CPUUsageChart 从 JavaFX 扩展了 Application,我从一个简单 Java 项目的 main 方法启动它。

我最终想要实现的是,我可以在简单的Java代码中启动App并使用它的方法,这样我就不必在Application扩展类的Constructor中进行调用了。我只想使用 JavaFX 的能力来绘制图表并将它们保存到 HDD 以供以后使用,但我不需要看到任何用 JavaFX 制作的 GUI。

【问题讨论】:

    标签: java charts javafx launch


    【解决方案1】:

    建议的解决方案

    您可以only launch an application once,因此您的应用程序类将永远只有一个实例。

    由于应用程序只有一个实例,因此可以在应用程序启动时将对该实例的引用存储在应用程序的静态变量中,并且可以根据需要从静态方法(一种单例模式)。

    注意事项

    必须注意确保:

    1. 实例在您尝试使用之前可用。
    2. 适当遵守线程规则。
    3. JavaFX 平台在不再需要时适当关闭。

    示例解决方案

    下面的示例代码使用一个锁和一个条件来确保应用程序实例在您尝试使用它之前可用。当不再需要 JavaFX 平台时,它还需要显式关闭。

    感谢 StackOverflow 用户 James-D 对此代码的一些编辑帮助。

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.collections.ObservableList;
    import javafx.embed.swing.SwingFXUtils;
    import javafx.scene.Scene;
    import javafx.scene.chart.LineChart;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.XYChart;
    import javafx.stage.Stage;
    import javax.imageio.ImageIO;
    import java.io.File;
    import java.io.IOException;
    import java.nio.file.Paths;
    import java.time.LocalDateTime;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class CPUUsageChart extends Application {
        private static CPUUsageChart appInstance;
    
        private static final Lock lock = new ReentrantLock();
        private static final Condition appStarted = lock.newCondition();
    
        /**
         * Starts the application and records the instance.
         * Sets the JavaFX platform not to exit implicitly. 
         * (e.g. an explicit call to Platform.exit() is required
         *       to exit the JavaFX Platform).
         */
        @Override 
        public void start(Stage primaryStage) {
            lock.lock();
    
            try {
                Platform.setImplicitExit(false);
                appInstance = this;
                appStarted.signalAll();
            } finally {
                lock.unlock();
            }
        }
    
        /**
         * Get an instance of the application.
         * If the application has not already been launched it will be launched.
         * This method will block the calling thread until the
         * start method of the application has been invoked and the instance set. 
         * @return application instance (will not return null).
         */
        public static CPUUsageChart getInstance() throws InterruptedException {
            lock.lock();
    
            try {
                if (appInstance == null) {
                    Thread launchThread = new Thread(
                            () -> launch(CPUUsageChart.class), 
                            "chart-launcher"
                    );
                    launchThread.setDaemon(true);
                    launchThread.start();
                    appStarted.await();
                }
            } finally {
                lock.unlock();
            }
    
            return appInstance;
        } 
    
        /**
         * Public method which can be called to perform the main operation 
         * for this application.
         * (render a chart and store the chart image to disk).
         * This method can safely be called from any thread.
         * Once this method is invoked, the data list should not be modified
         * off of the JavaFX application thread.
         */
        public void renderChart(
            ObservableList<XYChart.Data<Number, Number>> data
        ) {
            // ensure chart is rendered on the JavaFX application thread.
            if (!Platform.isFxApplicationThread()) {
                Platform.runLater(() -> this.renderChartImpl(data));
            } else {
                this.renderChartImpl(data);
            } 
        }
    
        /**
         * Private method which can be called to perform the main operation 
         * for this application.
         * (render a chart and store the chart image to disk).
         * This method must be invoked on the JavaFX application thread.
         */
        private void renderChartImpl(
            ObservableList<XYChart.Data<Number, Number>> data
        ) {
            LineChart<Number, Number> chart = new LineChart<>(
                    new NumberAxis(),
                    new NumberAxis(0, 100, 10)
            );
            chart.setAnimated(false);
            chart.getData().add(
                    new XYChart.Series<>("CPU Usage", data)
            );
    
            Scene scene = new Scene(chart);
    
            try {
                LocalDateTime now = LocalDateTime.now();
                File file = Paths.get(
                        System.getProperty("user.dir"),
                        "cpu-usage-chart-" + now + ".png"
                ).toFile();
                ImageIO.write(
                        SwingFXUtils.fromFXImage(
                                chart.snapshot(null, null),
                                null
                        ),
                        "png",
                        file
                );
    
                System.out.println("Chart saved as: " + file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    要使用它(从任何线程):

    try {
         // get chartApp instance, blocking until it is available.
         CPUUsageChart chartApp = CPUUsageChart.getInstance();
         // call render chart as many times as you want
         chartApp.renderChart(cpuUsageData);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
         // note your program should only ever exit the platform once.
         Platform.exit(); 
    }
    

    完整的示例应用程序,它创建五个 CPU 使用数据图表,每个图表中有十个样本,每个样本间隔 100 毫秒。当示例调用图表应用程序来渲染图表时,它将在当前 java 工作目录中创建图表 png 图像文件,并将文件名输出到系统控制台。不显示 JavaFX 阶段或窗口。

    How to get percentage of CPU usage of OS from java复制的 CPU 使用率示例代码

    import javafx.application.Platform;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.chart.XYChart;
    
    import javax.management.*;
    import java.lang.management.ManagementFactory;
    
    public class ChartTest {
        public static void main(String[] args) {
            try {
                CPUUsageChart chart = CPUUsageChart.getInstance();
                for (int i = 0; i < 5; i++) {
                    ObservableList<XYChart.Data<Number, Number>> cpuUsageData = FXCollections.observableArrayList();
                    for (int j = 0; j < 10; j++) {
                        cpuUsageData.add(
                               new XYChart.Data<>(
                                       j / 10.0, 
                                       getSystemCpuLoad()
                               )
                        );
                        Thread.sleep(100);
                    }
                    chart.renderChart(cpuUsageData);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (MalformedObjectNameException | ReflectionException | InstanceNotFoundException e) {
                e.printStackTrace();
            } finally {
                Platform.exit();
            }
        }
    
        public static double getSystemCpuLoad() throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException {
            MBeanServer mbs    = ManagementFactory.getPlatformMBeanServer();
            ObjectName name    = ObjectName.getInstance("java.lang:type=OperatingSystem");
            AttributeList list = mbs.getAttributes(name, new String[]{ "SystemCpuLoad" });
    
            if (list.isEmpty())     return Double.NaN;
    
            Attribute att = (Attribute)list.get(0);
            Double value  = (Double)att.getValue();
    
            if (value == -1.0)      return Double.NaN;  // usually takes a couple of seconds before we get real values
    
            return ((int)(value * 1000) / 10.0);        // returns a percentage value with 1 decimal point precision
        }
    }
    

    样本输出(Y 轴上的 CPU 使用率百分比,以及 X 轴上以十分之一秒的采样间隔为单位的时间)。

    背景信息

    替代实现

    • 您可以使用JFXPanel 而不是扩展应用程序的类。不过,您的应用程序也会依赖于 Swing。
    • 您可以让应用程序的主类扩展 Application,这样应用程序就会在您的应用程序启动时自动启动,而不是为您的使用图表创建一个单独的应用程序。
    • 如果您有很多图表要呈现,您可以查看off screen chart renderer implementation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-06
      • 2012-01-06
      • 2018-04-22
      • 2013-03-17
      • 2012-12-02
      • 2014-11-05
      • 2012-10-30
      相关资源
      最近更新 更多