OSGi教程 – 03 – OSGi 的事件机制

3.1 事件的存在

在前两章,你已经了解了如何将服务声明者、实现者和使用者联系起来。显而易见,它们对服务声明部分是有较强的依赖性的,实现者和使用者双方的代码里,都出现了接口ISayAnyThing,尽管这看起来已经足够好了,但是正如我在 1.3 节中所说,这种依赖可能会带来Bundle的森林——因为使用相同的接口,实际上就是要求服务的提供者和使用者满足一种显式的一致性要求,当服务的提供者和使用者数目很多的时候,处理这种一致性要求的代价可能会很大,甚至会引入很多非技术性的问题——例如管理问题、文档问题。事件机制可以部分地解决这个问题——让Bundle产生一个事件,对该事件有兴趣的处理者,可以进行相关的处理——当然,这样也可能会带来反面的问题:对事件的处理过程可能会被隐藏起来,一旦事件机制被滥用,这会带来问题调试者的噩梦。

最常用的例子是有时候我们需要得到某些信息,那么就可以产生一个事件,由OSGi事件管理服务来自动分发事件给所有感兴趣的处理者,处理者在收到指定格式的事件之后即可进行处理——例如读取相关信息或向事件所关联的对象中填入合适的值——这样当所有的处理者都处理完之后,发送者就有可能根据对应对象值来判断事件被处理的情况。

20160724001

图3.1.1 事件的发布与处理

对于事件的发布者来说,可能并不清楚,或者是不必清楚事件由谁来处理,如何处理。而对于事件处理者来说,也无需清楚事件由谁来发布,如何发布(甚至连接口这种形式上的约束也不需要)。OSGi框架自动帮你保证消息的分发的有效性,帮你确定收发顺序、安全等。如果处理方式合适,也会很高效(因为在OSGi框架本身就有数目繁多的事件激发和处理,我们做过压力测试,每秒钟处理数以万计甚至更多消息是可能的)。事实上,事件的发布者和处理者使用了一种经典设计模式——观察者模式(或者叫发布-订阅模式)。在OSGi的事件机制中,实际上提供了一种约定(即统一的事件发布和处理接口,而不是各种自定义的服务接口),来使得事件的发布者和处理者的工作方式统一,也就进一步降低了事件处理双方的耦合性(当然,由API级别的约束转为文档级的约束,不见得都是好事,看你怎么把握了)。关于观察者模式的细节,这里不作过多描述,你若有兴趣,可以去参考相关书籍和资料。

当然事件机制的作用不仅仅是解决耦合性过多和过高的问题,几乎OSGi框架里所有的核心Bundle都会与事件打交道,要么发布事件,要么处理事件,对他们专业的称呼是Event Publisher和Event Handler。在所有Bundle生命周期的各个阶段之间变换时也均会产生事件,整个OSGi框架的变动也会产生事件……在这里我们不作过多描述(注意,不作过多描述不代表不重要,也不代表其作用范围不广泛,恰恰相反,这里面包含了太多太多的细节,值得好好研究,只是本章的核心在于演示自定义的事件而已)——关键在于我们自己的Bundle也可以利用事件机制来做很多事情。

我们对之前的例子进行进一步改造,NeedServiceBundle作为Event Publisher发布事件,ServiceBundle作为Event Handler处理事件,NeedServiceBundle在启动之后会告诉ServiceBundle自己已经启动了——要使用事件机制,需要用到OSGi框架的Event Admin服务,相关的定义在org.osgi.service.event包中。

3.2 事件的构成

Event对象有两个属性:Topic和Properties。

Topic是必须的,用来定义事件的类别,比如Bundle启动的时候会发布一个启动事件,其标题为:org/osgi/framework/BundleEvent/STARTED。我们可以采用类似的格式来对自己的事件命名,一般格式为:包名/类名/事件类别。

Properties可以有一个或多个,每个属性都是一对属性名和属性值。属性名是一个对大小写敏感的字符串;属性值可以是任何Java对象,不过建议尽量只使用String和其他基本类型及其包装类,这样可以减少因为多个Handler捕获事件后对事件属性内容修改而带来的潜在问题(比如到底谁改了,改得对不对)。

3.3 发布事件

本章的运行环境我们仍基于之前的OSGi Framework,在开始之前,首先按照之章节描述的方法,在名为“Chapter2”的OSGi Framework基础上复制一份,并命名为“Chapter3”。然后OSGI-INF/components2.xml删除(这是在之前的演示中我们故意重复声明的服务),并删除MANIFEST.MF文件中Service-Component条目下对应的值

要发布事件,首先要记得在open Debug Dialog或open Run Dialog的Bundles选项卡中勾选org.eclipse.equinox.event,将其加入运行环境。

20160724002

图3.3.1配置运行环境

然后在工作空间的NeedServiceBundle工程下双击MANIFEST.MF,进入属性编辑器后在Dependencies选项卡的Imported Packages中新增org.osgi.service.event包。

20160724003

图 3.3.2 导入 event 包

下面我们将对NeedServiceBundle工程进行改造,使其在SingASong类中发送事件,将SingASong类的activeate方法改造成如下:

为了保证代码可运行,注意导入如下包:

根据上面代码中的注释可以看到事件消息的发送有两个方法:sendEvent和postEvent。前者是同步调用,当所有的EventHandler处理完毕后才会返回;后者是异步调用,不必等待EventHandler的返回——如果不是必须使用前者来同步调用,建议使用后者,其代价远小于前者。

3.4 处理事件

下面我们来改造ServiceBundle,使其可以处理事件(也就是接收消息),与改造NeedServiceBundle类似,首先需要导入org.osgi.service.event包,如下图:

20160724004

图3.4.1 为ServiceBundle导入event包

然后让Activator 类实现EventHandler接口并编写handleEvent方法,最后在start方法中注册监听服务即可。

备注:
我们采用 Activator 类来监听事件只是因为在本例中操作比较简单,事实上你可以使用其它类来做同样的事。并且要注意在implements关键字后加上EventHandler接口,否则会在运行时获得关于类型不符的错误提示。

经过改造后的Activator类主要部分是这样的:

正常运行起来后,你应该看到类似这样的界面:

20160724005

图3.4.2消息机制样例运行界面

源码下载

打赏一下
支付宝
微信
除非注明,博客文章均为原创,转载请标明文章地址
本文地址: http://www.javafxchina.net/blog/2016/07/osgi-03/
百度已收录