目录
CTK Plugin Framework简介
框架简介
1.1、简介
1.2、插件模块层
1.3、服务层
1.4、生命周期层
2、框架实现
2.1、Plugin System
2.2、Service Registry
3、框架优点
3.1、降低复杂性
3.2、可复用
3.3、版本控制
3.4、动态更新
3.5、自适应
3.6、透明性
3.7、开发简单
3.8、懒加载
3.9、非独占性
3.10、非侵入
4、CTK事件管理机制
4.1、框架事件
4.2、插件事件
4.3、服务事件
4.4、自定义事件
二、CTK Plugin Framework架构及插件的使用
1、CTK Plugin Framework使用流程
2、创建CTK插件
3、CTK插件框架使用实例
参考
CTK Plugin Framework设计参考了OSGi(Open Service Gateway Initiative,开放服务网关协议)(Java的动态组件系统),并提供了一种能让应用程序(动态地)由许多不同的可重用组件组成的开发模型,允许通过服务进行通信。
OSGi规范的核心部分是一个框架,核心框架定义了应用程序的生命周期模式和服务注册。基于OSGI核心框架定义了大量的OSGi服务:日志、配置管理、HTTP(运行servlet)、XML分析、设备访问、软件包管理、许可管理、用户管理、IO连接、连线管理、Jini和UPnP。
CTK Plugin Framework框架的分层模型如下:
Plugins(插件):由开发人员创建的CTK组件;
Services Layer(服务层):通过为C++对象提供一个publish-find-bind模型,以动态方式连接插件;
Life Cycle Layer(生命周期层):用于安装、启动、停止、更新和卸载插件的API;
D、Security(安全性):处理安全方面。
Plugin是CTK Plugin Framework的核心,是模块化特性的体现。
插件由插件激活器类Activator启动,激活器可以获取代表插件框架的插件上下文环境,插件上下文对象不能共享。
Plugin是基于C++/Qt的一个共享库,包含了资源文件和元数据(metadata)。元数据的目的在于准确描述Plugin的特征,除了让CTK Plugin Framework对Plugin适当地进行各种处理(例如:依赖解析)外,还能更好的对Plugin进行标识,以帮助用户对Plugin进行理解。
元数据被定义在MANIFEST.MF文件中,典型的MANIFEST.MF文件如下:
Plugin-SymbolicName: HelloCTK
Plugin-ActivationPolicy: eager
Plugin-Category: demo
Plugin-ContactAddress: https://github.com/scorpiostudio
Plugin-Description: A plugin for say hello
Plugin-Name: HelloCTK
Plugin-Vendor: scorpio
Plugin-Version: 1.0.0
元数据主要分为两部分:
一、Plugin的标识符(必须):唯一标识一个 Plugin,由Plugin-SymbolicName表示。
二、可读信息(可选):帮助更好地理解和使用Plugin,不对模块化特性产生任何的影响。可选信息如Plugin-Name、Plugin-Vendor。
CTK插件框架提供了插件间通信的动态服务模型,一个激活的插件可以在任何时候注册(注销)0个或多个服务到框架。服务注册是一个具有可选注册属性的发布接口。通过接口和过滤表达式可以从插件框架获得服务引用。框架发布服务生命周期事件。
服务可以通过ctkPluginContext对象注册到插件框架。服务的注册和注销可以在任何时候进行。
服务是服务的提供者和使用者之间的一个契约,使用者一般不关心其实现的细节,只关心是否满足契约(服务应该提供什么功能、满足什么格式)。使用服务的过程包含了发现服务和达成协议的形式,即需要通过服务的标志性特征来找到对应的服务。
一个插件可以创建一个对象,并在一个或多个接口(通常是一个只有纯虚方法的C++类)下使用CTK Service Registry注册它。其它插件可以要求registry列出在特定接口下注册的所有服务(对象)。一个插件甚至可以等待一个特定的服务出现,然后收到回复。
因此,一个插件可以注册一个服务,也可以获得一个服务并侦听服务的出现或消失。任意数量的插件可以在相同的接口下注册服务,并且任意数量的插件都可以得到相同的服务。
publish-find-bind模型如下:
如果多个插件在同一个接口下注册对象,则可以通过其属性进行区分。每个服务注册都有一套标准的自定义属性,可以使用过滤器来选择感兴趣的服务。属性也可以被用于应用程序级的其他角色。
一、发布服务
为了让其它Plugin能发现服务,必须用上下文对其进行注册,需要用到接口名、服务对象(接口的具体实现)和一个可选的ctkDictionary类型的属性信息:
ctkDictionary properties;
properties.insert("name", "scorpio");
properties.insert("age", 30);
ctkServiceRegistration registration = context->registerService
得到一个ctkServiceRegistration对象,用于更新服务的属性:
registration.setProperties(newProperties);
注销服务:
registration.unregister();
registration对象不能和其它Plugin共享,因为registration对象和发布服务的Plugin的生命周期相互依存。如果Plugin已经不存在于框架执行环境中,那么registration对象也不应该存在。
此外,如果在删除发布的服务前Plugin停止,框架会帮助删除这些服务。
二、获取服务
一旦服务被发布,服务将对其他Plugin可用。获取服务的方式非常简单,只需要提供一个接口名即可:
ctkServiceReference reference = context->getServiceReference
reference对象是服务对象的间接引用。间接引用可以将服务的使用和服务的实现进行解耦。将服务注册表作为两者的中间人,不仅能够达到跟踪和控制服务的目的,同时还可以在服务消失以后通知使用者。
接口的返回类型是ctkServiceReference,可以在Plugin之间互享,因为reference对象和使用服务的Plugin的生命周期无关。
生命周期层主要用于控制Plugin的安装、启动、停止、更新和卸载,可以从外部管理应用或者建立能够自我管理的应用(或将两者相结合),并且给了应用本身很大的动态性。
Plugin的使用需要使用生命周期层的API来和CTK Plugin Framework的生命周期层进行交互。
Plugin生命周期的状态转换图:
生命周期层的API主要由三个核心部分组成:ctkPluginActivator、ctkPluginContext和ctkPlugin。
(1)、ctkPluginActivator
ctkPluginActivator:自定义plugin的启动和停止。
ctkPluginActivator是一个接口,必须由框架中的每个插件实现。插件必须提供一个由插件框架调用的插件激活器类。框架可以根据需要创建一个插件的ctkPluginActivator实例。如果一个实例的ctkPluginActivator::start()方法成功执行,则需要保证在插件停止时调用同一个实例的ctkPluginActivator::stop() 方法。
当插件进入ACTIVE状态时,框架会调用start方法,当插件离开ACTIVE状态时,插件框架会调用stop方法。每一个插件都会接收到一个访问插件框架的唯一ctkPluginContext对象。
(2)、ctkPluginContext
ctkPluginContext是一个plugin在框架内的执行上下文,用于授予对其它方法的访问,以便该插件可以与框架交互。ctkPluginContext提供的方法允许插件:
A、订阅由框架发布的事件;
B、使用Framework Service Registry注册服务对象;
C、从Framework Service Registry检索ServiceReferences;
D、为引用的服务获取和发布服务对象;
E、在框架中安装新的插件;
F、获取框架中安装的插件列表;
G、获得一个插件的ctkPlugin对象;
H、为(由框架为插件提供的)持久存储区域中为文件创建QFile对象。
当使用ctkPluginActivator::start()方法启动时,将创建一个 ctkPluginContext对象,并将其提供给与此上下文关联的插件。当使用ctkPluginActivator::stop()方法停止时,相同的ctkPluginContext对象将被传递给与此上下文关联的插件。ctkPluginContext对象通常用于其关联插件的私有用途,并不意味着与插件环境中的其它插件共享。
与ctkPluginContext对象关联的ctkPlugin对象称为上下文插件。
ctkPluginContext对象只有在它的上下文插件执行时才有效;即在上下文插件处于STARTING、STOPPING和ACTIVE状态的时段内。如果随后使用ctkPluginContext对象,则必须抛出一个ctkIllegalStateException异常。当上下文插件停止后,ctkPluginContext对象不能被重用。
Framework是唯一能够创建ctkPluginContext对象的实体,并且ctkPluginContext对象只在创建它们的Framework中有效。
(3)、ctkPlugin
ctkPlugin是Framework中已安装的插件。
ctkPlugin对象是定义一个已安装插件的生命周期的访问点,在插件环境中安装的每个插件都必须有一个相关的ctkPlugin对象。此外,插件必须有一个唯一的标识,在插件的生命周期中,唯一标识不能改变(即使是在插件更新时),卸载和重新安装插件必须创建一个新的唯一标识。
插件有以下状态(状态是动态可变的,在特定条件下可以互相转换):
UNINSTALLED
INSTALLED
RESOLVED
STARTING
STOPPING
ACTIVE
要确定插件是否处于有效状态之一,可以使用States类型进行“或”运算。
插件只能在状态为STARTING、ACTIVE或STOPPING状态时执行代码。一个UNINSTALLED插件是一个僵尸,不能被设置为另一个状态。
框架是唯一允许创建ctkPlugin对象的实体,并且ctkPlugin对象仅在创建它们的框架内有效。
CTK Plugin Framework基于Qt Plugin System和Qt Service Framework实现,并且增加了插件元数据(由MANIFEST.MF文件提供)、定义良好的插件生命周期和上下文、综合服务发现和注册特性来扩展。
在Qt Plugin System中,插件的元数据由JSON文件提供。CTK Plugin Framework的核心架构主要包含两个组件:Plugin System和Service Registry。这两个组件是相互关联的,在API级别上的组合使得系统更加全面、灵活。
CTK Core依赖于QtCore模块,因此CTK Plugin Framework基于Qt Plugin System。Qt API允许在运行时加载和卸载插件,热插拔功能在CTK Plugin Framework中得到了加强,以支持透明化延迟加载和解决依赖关系。
插件的元数据被编译进插件内部,可以通过API进行提取。此外,插件系统还使用SQLite缓存了元数据,以避免应用程序加载时间问题。另外,Plugin System支持通过中央注册中心使用服务。
Qt Service Framework是Qt Mobility项目发布的一个Qt 解决方案,Qt服务框架允许“声明式服务”和按需加载服务实现。为了启用动态(非持久性)服务,Qt Mobility服务框架可以与Service Registry一起使用。
CTK Plugin Framework以OSGi规范为模型,并实现了几乎完整的OSGI框架API,因此使用CTK Plugin Framework开发基于Qt的C++应用程序有如下优点:
使用CTK Plugin Framework进行应用开发只需进行插件开发,插件隐藏了内部实现,并通过定义良好的服务来和其它插件通信。隐藏内部机制意味着可以自由地更改实现,不仅有助于Bug数量的减少,还使得插件的开发变得更加简单,因为只需要实现已经定义好的一定数量的功能接口即可。
标准化的组件模型,在应用程序中使用第三方组件变得非常简单。
在CTK Plugin Framework中,所有的插件都经过严格的版本控制,只有能够协作的插件才会被连接在一起。
OSGi组件模型是一个动态模型,插件可以在不关闭整个系统的情况下被安装、启动、停止、更新和卸载。
OSGi组件模型是从头设计的,以允许组件的混合和匹配,要求必须指定组件的依赖关系,并且需要组件在其可选依赖性并不总是可用的环境中生存。Service Registry是一个动态注册表,其中插件可以注册、获取和监听服务。OSGI动态服务模型允许插件找出系统中可用的功能,并调整它们所能提供的功能,使得代码更加灵活, 并且能够更好地适应变化。
插件和服务是CTK插件环境中的一等公民。管理API提供了对插件的内部状态的访问,以及插件之间的连接方式。可以停止部分应用程序来调试某个问题,或者可以引入诊断插件。
CTK插件相关的API非常简单,核心API不到25个类。核心API足以编写插件、安装、启动、停止、更新和卸载,并且还包含了所有的监听类。
CTK Plugin Framework不仅仅是组件的标准,还指定了如何安装和管理组件的API。API可以被插件用来提供一个管理代理,管理代理可以非常简单,如命令shell、图形桌面应用程序、Amazon EC2的云计算接口、或IBM Tivoli管理系统。标准化的管理API 使得在现有和未来的系统中集成CTK Plugin Framework变得非常容易。
OSGi技术有很多的机制来保证只有当类真正需要的时候才开始加载插件。例如,插件可以用饿汉式启动,但是也可以被配置为仅当其它插件使用它们时才启动。服务可以被注册,但只有在使用时才创建。懒加载场景可以节省大量的运行时成本。
CTK Plugin Framework不会接管整个应用程序,可以选择性地将所提供的功能暴露给应用程序的某些部分,或者甚至可以在同一个进程中运行该框架的多个实例。
在一个CTK插件环境中,不同插件均有自己的环境。插件可以使用任何设施,框架对此并无限制。CTK服务没有特殊的接口需求,每个QObject都可以作为一个服务,每个类(包括非QObject)都可以作为一个接口。
框架使用事件机制来通知各个插件,系统中插件的安装、卸载、解析、启动、停止等状态的切换[5]。
在 CTK 中,插件之间的通信,可以通过 Event Admin 来完成。Event Admin 是一种基于发布/订阅的方式,一个插件订阅某一主题之后,另一个插件发布一个与该主题相关的事件,从而达到通信的目的。