[英]Handle on launched JavaFX Application
How do I get a handle on a JavaFX application started using the following code? 如何使用以下代码启动JavaFX应用程序的句柄?
CPUUsageChart.launch(CPUUsageChart.class);
CPUUsageChart extends Application from JavaFX and I am launching it from a main method of a simple Java project. CPUUsageChart从JavaFX扩展了Application,我从一个简单的Java项目的主要方法启动它。
What I ultimately want to achieve is, that I can start the App and use its methods in the simple Java code, so that I do not have to do the calling in the Constructor of the Application extending class. 我最终想要实现的是,我可以启动App并在简单的Java代码中使用其方法,这样就不必在Application扩展类的Constructor中进行调用。 I only want to use JavaFX's abilities for drawing charts and save them to HDD, for later usage, but I do not need to see any GUI made in JavaFX.
我只想使用JavaFX的功能来绘制图表并将其保存到HDD中,以备后用,但我不需要查看JavaFX中制作的任何GUI。
Proposed Solution 拟议的解决方案
You can only launch an application once , so there will only ever be a single instance of your application class. 您只能启动一个应用程序一次 ,因此将永远只有一个应用程序类实例。
Because there is only a single instance of the application, you can store a reference to the instance in a static variable of the application when the application is started and you can get the instance as required from a static method (a kind of singleton pattern). 因为只有一个应用程序实例,所以您可以在应用程序启动时将对实例的引用存储在应用程序的静态变量中,并且可以根据需要从静态方法(一种单例模式)中获取实例。 。
Caveats 注意事项
Care must be taken to ensure: 必须注意确保:
Sample Solution 样品溶液
The sample code below uses a lock and a condition to ensure that the application instance is available before you try to use it. 下面的示例代码使用锁和条件来确保在尝试使用应用程序实例之前该应用程序实例可用。 It will also require explicit shutdown of the JavaFX platform when it is no longer required.
当不再需要JavaFX平台时,它也需要显式关闭。
Thanks to StackOverflow user James-D for some edit assistance with this code. 感谢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();
}
}
}
To use this (from any thread): 要使用它(从任何线程):
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();
}
Complete sample application which creates five graphs of cpu usage data with ten samples in each chart, each sample spaced by 100 milliseconds. 完整的示例应用程序,它将创建五张cpu使用情况数据图形,每个图表中包含十个示例,每个示例之间的间隔为100毫秒。 As the sample invokes the chart application to render the charts, it will create chart png image files in the current java working directory and the file names will be output to the system console.
当示例调用图表应用程序渲染图表时,它将在当前java工作目录中创建图表png图像文件,并且文件名将输出到系统控制台。 No JavaFX stage or window is displayed.
没有显示JavaFX阶段或窗口。
Code to sample CPU usage copied from: How to get percentage of CPU usage of OS from java 复制以下示例中的CPU使用率的代码: 如何从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
}
}
Sample output (percentage CPU usage on the Y axis, and time in tenth of second sample spacing on the X axis). 样本输出(Y轴上的CPU使用率百分比,X轴上的时间以十分之一秒为单位)。
Background Information 背景资料
Alternate Implementations 替代实现
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.