GUI桌面编程这个话题本来就非常不好写。这么多年,这么大一个领域,真真正正能写成像《深入浅出MFC》这样的经典的书籍没有几本。纵观国内在该领域出版的书籍,要么就是大篇大篇罗列这个控件那个控件怎么用,控件有哪些属性可以设置,有哪些事件可以处理;要么就是大段大段贴代码,而对于GUI编程的一些本质、一些哲学性的东西没有深入的分析。在Eclipse RCP领域尤其如此,有那么几本打着Eclipse RCP和OSGi大旗的书籍,但是其参考价值却不比Eclipse自带的帮助文档大多少,大有圈钱的嫌疑。
Eclipse RCP是个好东西。用Eclipse RCP写桌面程序,可以在一个很高的起点上构建自己的窗口应用,至于那种从头开始写窗口的日子是再也不复返了,甚至美工都不需要自己考虑了。(想想VC领域有多少界面库在折磨着程序员。)还有一个好处当然是跨平台了,虽然要背上一个Java虚拟机的负担。(现在的Eclipse RCP程序导出时可以自己包含JRE,又大大简化了程序部署的过程。)
五年前我写的那几篇博文有很多缺点,缺点一是其中有些东西我自己都没有搞懂,所以没办法写得太深入;缺点二是跳跃性太大,总共才六篇,我却又是展示COM组件,又是展示OpenGL,虽说让大家在视觉上是狠狠体验的一把,但是一点也不系统。所以这次一定要改变这些缺点,争取做到以下目标:
目标:
1.言简意赅,对于那些有助于理解Eclipse RCP平台的核心概念重点点出,并结合实例。对于那种在Google上一查一大把的废话坚决不写;
2.争取系统性地介绍Eclipse RCP平台可用的特性及其背后的一些设计模式,不再浮光掠影地一飘而过,也不专写冷门偏门;
3.不追求面面俱到,有些用不到的东西肯定没必要讲,也不怕别人说我水平差;
4.当然是大量展示图片啦。
好了,下面开工,首先,当然是创建一个Hello World,初步展示一些Eclipse RCP的效果。Eclipse RCP编程的本质就是利用Eclipse的插件机制,在Eclipse的Runtime上进行扩展来构建我们自己的窗口程序,所以,创建项目的时候当然是创建一个Plug-in Project了。只是在下面那个“Would you like to create a rich client application ?”的地方选择yes即可。
一路Next下去,我就不多截图了。在选择应用程序模板的时候,选择最简单的Hello RCP即可。如果这时运行程序(用Run As -> Eclipse Application),可以得到一个简单的窗口。
为了显示Hello World,我们需要一个扩展一个View。创建一个View类,其基类为org.eclipse.ui.part.ViewPart,如下图:
在HelloWorldView的代码中,我们只需要一个SWT中的Label控件用来显示HelloWorld,如下图:
下一步,就要把这个HelloWorldView插入到窗口中,这个可以通过配置Plugin的Extension来实现。打开plugin.xml的编辑器,增加一个org.eclipse.ui.views的扩展,如下图:
在Eclipse中,扩展的View并不是马上显示到前台,而是要通过菜单Window->Show View来显示,或者将View关联到某个透视图,切换透视图的时候显示这个View。我们的RCP程序的目标是单独运行,而不是当插件安装到Eclipse中,追求的是一运行程序就显示这个View,所以还要扩展org.eclipse.ui.perspectiveExtensions。如下图:
运行程序,看看效果。通过Run As -> Eclipse Application运行,如下图:
到现在,我们的工作完成一半,因为该程序还只能在Eclipse中运行,要想脱离Eclipse单独运行,必须将其作为单独的产品导出。要导出产品,先要新建一个product configuration,然后进行配置。为简单起见,我们只需要配置三个地方。
第一个地方:Application,这个关系到程序的启动点
第二个地方,launcher,这个可以生成一个像eclipse那样的本地启动程序
第三个地方,依赖项,可以先把自己添加到依赖项中,然后再点Add Required Plug-ins按钮添加所有需要的依赖项
我自己第一次接触Eclipse RCP时,就经常因为不把自己添加到依赖项中而造成程序启动失败。
下一步,导出程序,如下图:
在以上对话框中,我把能省的空都省了。最后得到的目标程序文件夹内容及运行程序的效果如下图:
以上过程非常简单,所以先秀图片。下面,来看看Eclipse RCP和OSGi的一些概念。
1.Bundle和Bundle的生命周期:Bundle是OSGi的一个概念,我们可以把它理解为模块、插件,如果仅仅只是学习Eclipse RCP编程的话,就不需要对OSGi进行深入学习,虽然它很流行。但是一些基本的概念还是要知道的。Eclipse完全构建在OSGi之上,它包含有一个OSGi规范的完整实现Equinox。所以Eclipse的一个Plug-in,基本上就等于OSGi的一个Bundle。我们不需要特意去编辑Bundle的定义文件META-INFO/MANIFEST.MF,在编辑plugin.xml的时候,Bundle的定义文件会自动更新。一个Bundle被安装到OSGi系统中后,并不会马上启动,只有当这个Bundle提供的功能被调用的时候,该Bundle才会被启动,当该Bundle不被用到的时候,它就可以被停止。在一个Bundle的生命周期中,我们可以定义自己的Activator来处理启动、停止等各个阶段的任务,这就是我们前面的例子中为什么要定义一个Activator的原因。通过Activator,我们可以查看OSGi系统中安装的所有Bundle,因为OSGi系统调用Activator中的方法时,会传递一个BundleContext参数,使用该context,我们可以完全访问整个OSGi系统的功能。
2.Plug-in之间或Bundle之间的依赖关系:由于Plug-in和Bundle基本上是同义词,所以在后文中我将不提Bundle。Eclipse的Plug-in机制早已深入人心,一个Plug-in经常会依赖于其它的Plug-in,这已经成为常识,不需要我赘述。Plug-in的定义中最重要的就是它的ID和Version,靠这两个字段就可以唯一标识一个Plug-in,所以在前面的例子中,凡是出现ID的地方,我都很认真填写,并且使用com.xkland.....这样的形式来定义ID,避免出现冲突。对于Name、Vendor这样的字段,明显是给人阅读的,所以就随便填一填。
3.还有比Plug-in更小的单位,那就是Package:虽然说Eclipse中的Plug-in是一个很小的单位了,但是还有更小的,那就是Package。如果有人不想依赖整个Plug-in的话,也可以单独只Import几个Package。当然,对于我们自己写的Plug-in,也可以export一些Package供别人使用。在plugin.xml的编辑器中,有一个专门的页面是用来设置和Package相关的内容的。在前面的例子中没有演示这一点,因为我觉得我写的程序还没有必要分这么细。
4.Extension和Extension Point:这个好像也早就是Eclipse领域常识性的概念了吧。在Plug-in中可以定义一些Extension Point,在别的Plug-in中可以扩展这些Extension Point。比如在前面的例子中,我们自己的Plug-in扩展了org.eclipse.core.runtime插件(叫Bundle可能更合适)和org.eclipse.ui插件定义的几个扩展点,这些定义实在plugin.xml编辑器的Extensions页面定义的。当然,我们的程序没有定义自己的Extension Point,因为不需要让别人去扩展什么。
5.Product不是个什么重要的概念,从前面的例子可以看出,我们定不定义Product的ID都没什么影响。不过要导出Product的时候,application一定要指定,依赖项也要搞清楚,因为这关系到我们的程序能不能成功运行。
好了,概念性的东西就将这么多。OSGi不需要深究,因为我们会直接和Eclipse的Runtime打交道,以后会有专门的章节讲Eclipse的Runtime提供有哪些服务。下面的例子演示即使不使用Native launcher,也可以通过OSGi框架启动我们的Hello World。
在导出Product的时候,选择如下图中的单选框和填写好根目录:
就可以得到一个Repository,该Repository是供p2安装程序使用的,里面没有Native launcher。如下图:
在这个目录里面建立一个configuration文件夹,里面放一个config.ini,记住,在Linux系统下,configuration文件夹一定要是rwx权限的哦。config.ini文件内容如下:
该文件定义了OSGi启动时安装哪些Bundle,并且这些Bundle默认的启动级别是多少。启动级别的数字越小的越优先启动。configuration/config.ini是OSGi启动时默认的配置文件路径,当然也可以使用-configuration选项更改为别的路径。然后,如下命令启动HelloWorld:
在指定启动OSGi的jar文件时,我偷了点懒,为了少敲几下键盘,使用了一个通配符,大家还是可以很轻松地看出来是哪个文件哦?Equinox的官方文档在这里:http://eclipse.org/equinox/documents/quickstart-framework.php
如果想按照Equinox的官方文档启动OSGi框架的命令行,还得从Eclipse的安装目录找到如下几个文件:
然后还需要这样一个config.ini文件:
<a href="mailto:osgi.bundles=./org.apache.felix.gogo.runtime_0.10.0.jar@start,\">osgi.bundles=./org.apache.felix.gogo.runtime_0.10.0.jar@start,\</a>
<a href="mailto:./org.apache.felix.gogo.command_0.12.0.jar@start,\">./org.apache.felix.gogo.command_0.12.0.jar@start,\</a>
<a href="mailto:./org.apache.felix.gogo.shell_0.10.0.jar@start,\">./org.apache.felix.gogo.shell_0.10.0.jar@start,\</a>
<a href="mailto:./org.eclipse.equinox.console_1.0.0.dist.jar@start,\">./org.eclipse.equinox.console_1.0.0.dist.jar@start,\</a>
osgi.console.enable.builtin=false
osgi.console=<port>
才能够按照Equinox的文档那样用java -jar org.eclipse.osgi_3.8.1.dist.jar -console启动OSGi框架的命令行。
从Hello World这个例子可以看出,我们的Eclipse RCP程序构建在org.eclipse.core.runtime和org.eclipse.ui这两个Bundle上。下一篇,我将为大家讲述这两个Bundle能为我们提供哪些服务。