OSGi 框架支持以下生命周期层事件:
l BundleEvent — 报告bundle的生命周期改变
l FrameworkEvent — 报告框架的启动、启动级别的改变、包的更新或者是捕获的错误。
事件的实际类型可以通过getType方法获得。返回的是一个整形数据,在类中定义了整形数据的含义。因此,可以在以后对事件进行扩充。忽略处理不可识别的事件。
1. 监听器
每一种类型的事件对应有一个监听器接口。下面描述了这些监听器:
l BundleListener和SynchronousBundleListener — 当bundle的生命周期信息改变后使用一个BundleEvent类型的事件来调用。
对SynchronousBundleListener的调用是同步的,在处理这个事件的时候,必须要先于调用其他任何BundleListener对象。下面描述了当框架进入到另一个状态时,发出的一系列的事件:
l INSTALLED – 框架安装后发出。
l RESOLVED– 框架解析一个bundle后发出。
l STARTING – 当框架即将启动一个bunlde时发出。只发送到SynchronousBundleListener对象。
l STARTED – 当框架已经启动了一个bundle后发出。
l STOPPING – 当框架即将停止一个bundle时发出。只发送到SynchronousBundleListener 对象。
l STOPPED– 当框架停止了一个bundle时发出。
l UNINSTALLED – 当框架卸载了一个bundle后发出。
l UNRESOLVED – 当框架检测到一个bundle变成不可解析的时候发出;当刷新或者更新一个bundle的时候发生这样的事件。当使用包管理API来更新一系列的bundle时,那么序列中的每一个bundle都有抛出一个UNRESOLVED BundleEvent事件。这个事件必须要等到序列中所有的bundle都已经停止,并且序列中没有任何bundle重新启动才抛出,这是由于使用同步bunle监听器。RESOLVED 和UNRESOLVED并不需要成对出现。
l UPDATED – 在bundle更新之后发出。
l FrameworkListener — 发生FrameworkEvent类型的事件后调用。
框架事件有以下类型:
l ERROR – 需要操作者立即处理的重要错误。
l INFO – 在特殊情况下需要的一般的信息。
l PACKAGES_REFRESHED – 框架更新了包。
l STARTED – 框架已经完成了初始化,正在普通模式下运行。
l STARTLEVEL_CHANGED – 在设置和处理一个新的启动级别后由框架发出。
l WARNING – 警告,提示操作者不是至关重要的但是存在潜在错误的信息。
在接口BundleContext中定义了可用于添加和移出每一种类型的监听器的方法。
除非一些特殊情况,事件是可以进行异步处理的,也就说并不是必须要使用和产生事件的同一个线程中来处理事件。并没有定义事件监听器的线程。
如果对于监听器的回调产生了不可检查的异常,那么在框架中必须要抛出一个框架事件FrameworkEvent.ERROR。除非回调发生在传递事件FrameworkEvent.ERROR过程中(为了防止死循环)。
2. 事件发送
如果框架是异步传递事件的,那么框架必须:
l 在事件传递之前,保存一个事件发生时的监听器列表的快照(而不是在以后进行是在事件发送之前)。这样监听器就在事件发生之后不需要访问列表。
l 确保在保存快照的时候监听器所属的bundle处于活动状态。
如果框架在事件发生时没有捕获到当前的监听器列表,而是等到事件传递之前进行,那么可能导致以下错误:一个bundle已经启动并注册了一个事件监听器,bundle就可以通过自己的BundleEvent.INSTALLED来查看自己的事件。
下面三种情景说明了上述的情况:
1. 情景一事件顺序:
l 发生事件A
l 注册监听器1
l 尝试异步传递事件A
异常状态:监听器1不能接收到事件A,由于它不是在事件发生前注册的。
2. 情景二事件顺序:
l 注册监听器2
l 发生事件B
l 取消注册监听器2
l 尝试异步传递事件B
异常状态:监听器2不能接收到事件B,由于监听器2时在事件B发生时注册的。
3. 情景三事件顺序:
l 注册监听器3
l 发生事件C
l 停止注册监听器3的bundle
l 尝试异步传递事件C
异常状态:监听器3肯定不能接收到事件C,这是由于它的BundleContext对象不存在。
3. 同步处理缺陷
通常,调用监听器的bundle不应该保留有任何Java监控器。这也就是说在初始化回调时,框架和同步事件的发送者都应该不是处于监控器之中。
Java监控器的目的在于保护更新数据的结构。也就是一小段不能调用任何不可监视代码的代码。在同步代码中调用OSGi框架可能导致不可预测的结果。其中之一有可能是导致死锁。死锁是指两个线程由于相互等待而阻塞。
可以通过超时来解决死锁问题,但是java监控器并没有使用超时。因此,线程一直挂起直到系统重新设置(Java中不赞成所有能停止一个线程的方法)。这种死锁的预防方法就是在同步代码中不调用框架(否则其他代码可能导致回调)。
如果再调用其他代码时,必须要使用锁,那么使用Java监控器来创建一个信号量,这个信号量可以超时,以此来预防死锁。