OSGi教程 – 05 – 在OSGi环境中使用JavaFX
本章将介绍如何在OSGi环境中启动JavaFX程序,并在JavaFX程序中调用OSGi服务。
JavaFX是在Java7中开始引入的客户端技术,与传统的Java客户端技术AWT/SWING或SWT相比,它除了使用代码来绘制界面外,还引入了CSS、XML、JavaScript脚本等技术,使得Java客户端技术向前迈进了一大步,与WPF、Silverlight、Flash等桌面技术终于有机会一较长短。虽然未来在Web桌面端中HTML5将逐步占据统治地位,但本地化桌面技术由于其优秀的本地资源调用能力、更好的源代码保护能力和无需服务器即可运行的特性,始终会在企业应用领域占有一席之地,而JavaFX技术由于经过了大幅的现代化改进,相信也会得到长足的发展。对于JavaFX桌面技术的应用不在本文的范围之内,具体可以参考www.javafxchina.net或其它相关书籍、站点。
5.1 创建JavaFX样例程序
一般JavaFX程序的入口类必须继承javafx.application.Application类,下面是一个简单的JavaFX应用样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class JavaFXSample extends Application { @Override public void start(Stage primaryStage) throws Exception { BorderPane borderPane = new BorderPane(); Button btn = new Button("test"); btn.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { System.out.println("按钮被按下"); }; }); borderPane.setCenter(btn); primaryStage.setScene(new Scene(borderPane, 640, 480)); primaryStage.show(); } public static void main(String[] args) { launch(args); } } |
运行上述程序界面如下图所示:
5.2 外部调用运行JavaFX样例程序
在上例中可以看到,在Application子类的main方法中调用父类的launch()方法即可启动JavaFX程序,如果要在OSGi环境中运行该程序,则首先需解决在其它类中调用Application子类的问题。但如果我们尝试在其它类中调用该类的launch()方法,代码如下:
1 2 3 4 5 6 7 |
public class Test { public static void main(String[] args) { JavaFXSample.launch(args); } } |
在我们尝试运行程序时会在控制台看到异常提示信息,如图:
那在OSGi环境中应如何启动该类呢?其实,JavaFX程序还提供了另外一种启动方式,调用Application的launch(Class APPClass,String… args)方法,在appClass参数中指定继承了Application类的子类即可。该方法声明如下图所示:
使用这种方法修改代码如下:
1 2 3 4 5 6 7 |
public class Test { public static void main(String[] args) { JavaFXSample.launch(JavaFXSample.class, args); } } |
以上代码可以在其它类中正常启动JavaFX应用程序,这样我们就可以在OSGi环境中适当的地方(例如Activator类的start方法中)用上述方式来启动JavaFX应用程序了。
5.3 添加JavaFX库
接下来要解决的是在OSGi环境中引用JavaFX库的问题。虽然JavaFX已经随JDK一起发布了,但在OSGi环境中却无法直接引用JavaFX相关类。因此,需要我们手动去Java安装目录下拷贝JavaFX的jar包,它位于Java安装目录\jre版本\lib\ext\jfxrt.jar(例如在我的机器上它位于C:\Program Files\Java\jre1.8.0_102\lib\ext\ jfxrt.jar)。
首先在自己的的OSGi工程下新建文件夹lib,将jfxrt.jar放到lib文件夹中,然后打开OSGi工程的MANIFEST.MF文件,切换到Runtime选项卡,点击Classpath区域的Add…按钮,然后在弹出的浏览对话框中选择刚刚拷贝的jfxrt.jar文件,现在你的工作空间如下图所示:
5.4 在OSGi环境中启动JavaFX应用线程
经过上述操作以后,我们已经能够在OSGi环境中正确编译启动JavaFX应用程序了。但如果在Bundle的Activator类的start方法中运行launch()方法会阻塞住,导致后面的代码无法运行,因此我们应该在单独的线程中调用JavaFX应用程序,这样才不会阻塞OSGi的主线程。启动代码如下:
1 2 3 4 5 6 7 8 |
public void start(BundleContext bundleContext) throws Exception { Thread t = new Thread() { public void run() { JavaFXSample.launch(JavaFXSample.class); }; }; t.start(); } |
5.5 在JavaFX应用程序中调用OSGi服务
样例代码中提供了javafx-osgi-service工程,其中声明了一个ITestService服务,考虑到通过对前面几章的学习,你已经掌握了OSGi服务的声明,这里就不再赘述关于ITestService的声明以及OSGi环境配置相关知识,请你根据之前的学习自行调试。
为了便于JavaFX程序中调用OSGi服务,最简单的方法是在Bundle启动时先通过API获取服务并将其引用赋值给一个静态属性(或通过DS的方式将服务注入到对应的静态属性中),然后再提供静态方法来供JavaFX应用程序访问。
以下代码是直接使用Activator类中的静态属性和静态方法来获取ITest服务的样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Activator implements BundleActivator { private static ITestService testService; public static ITestService getTestService() { return testService; } public void start(BundleContext bundleContext) throws Exception { ServiceReference<ITestService> sr = bundleContext.getServiceReference(ITestService.class); if (sr == null) { System.err.println("未获取到ITest服务"); } else { testService = bundleContext.getService(sr); System.out.println("获取服务成功"); } Thread t = new Thread() { public void run() { JavaFXSample.launch(JavaFXSample.class); }; }; t.start(); } public void stop(BundleContext bundleContext) throws Exception { } } |
同时对JavaFX应用程序的按钮点击事件进行改造,在其中调用OSGi服务:
1 2 3 4 5 6 7 |
btn.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { ITestService test = Activator.getTestService(); test.test(); System.out.println("成功调用了ITest服务"); }; }); |
相信你会再次看到熟悉的JavaFX应用界面。

