网上对于 OSGi 的资料可谓不多,自从 SpringDM 停止维护后、国内的应用文章更加变少。
今天写的是基于 eclipse 开发环境(equinox 核心)的、非 SCR 环境(即:不依赖注解的)的OSGi事件注册和服务调用。相比 Spring 框架的 “一体机” 打包策略,OSGi 保留了手动编写和定制系统的余地、且自带服务管理的功能。整个运行环境也很精简,基本类在 equinox.jar 里面。
首先是我整理的 OSGi 组件的基本关系图。和手表 / 手机 / 其他机器的设计一样,都有一个核心(的逻辑)、包含一些基础的功能。在这个核心的基础上,我们扩展这些基础的功能、达到设计不同应用的目的。基本关系如下图所示。绿色虚线代表需要传入的参数 / 红虚线代表调用后获得的参数:

【1】首先是 Bundle 的启停问题。以简单的流程讲,一个 Bundle 可以由某个 BundleActivator 调用。当 BundleActivator 激活时,系统将调用其 start 方法。当 BundleActivator 停止时(如:通过 gogo command 或者 equinox OSGi 命令行发送停止指令),系统将调用其 stop 方法。
这两个方法都有一个 BundleContext 参数,代表当前的 Bundle。
BundleActivator 激活,是依靠初次安装或通过 gogo command / equinox OSGi 命令行发送启动指令。BundleActivator 停止,是依靠 gogo command / equinox OSGi 命令行发送停止指令。
【2】解决了 Bundle 的启停问题,我们可以开始调用服务了。服务的形式必须是一个接口定义方法、外加一个类来实现这个接口。
编写了服务之后,我们需要先主动注册服务、(调用 BundleContext 或 Bundle 的 registerService() )才能进行调用。*当服务和 BundleActivator 是同一个类时,则不需要主动注册服务。
当我们不再需要这个服务时,可以通过代码(BundleContext 或 Bundle 的 ungetService(),或者 ServiceRegistration 的 unregister())主动取消注册。
【3】注册之后,想调用当前或其他 Bundle 包含的服务,需要用到 ServiceReference 对象。ServiceReference 对象是一个泛型、包装了【2】处编写的服务的接口。当我们注册之后,系统返回一个 ServiceRegistration 对象。调用返回的 ServiceRegistration 对象的 getReference() ,就可以获得 ServiceReference 对象。
OSGi 在这一步提供了参数互换的办法:调用 BundleContext 的 getService() 方法获得【2】处编写的服务,需要传入 ServiceReference 对象作为参数。而调用 BundleContext 的 getServiceReference() 方法获得 ServiceReference 对象,需要传入 【2】处编写的服务的接口 作为参数。
【4】重新获得【2】处编写的服务后,我们就可以按需调用服务中已有的方法 / 功能了。
【5】在默认情况下,系统按照 注册(REGISTERED)-> 修改(MODIFIED)->修改至不符合监听条件(MODIFIED_ENDMATCH)-> 取消注册(UNREGISTERING) 的几个阶段运转这些 Bundle。如果我们要针对某个服务的某个阶段,增加一些额外的操作、可以在获得 ServiceReference 对象后,实例化一个 ServiceListener 对象;并重写该对象的 serviceChanged 方法。重写的作用是,视需要在全部阶段 / 某些阶段中,通过作为参数的 ServiceEvent 对象获得 ServiceReference、再通过 ServiceReference 获取服务接口做操作。
先写到这里,有案例的再补充下。:)
*参考资料:
OSGi Core Release 5
https://docs.osgi.org/javadoc/r5/core/
(完)