×
Namespaces

Variants
Actions
Revision as of 06:44, 10 November 2011 by hamishwillee (Talk | contribs)

Qt Service Framework 文档

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

兼容于
平台:
Symbian

文章
flycarl 在 08 Jan 2011 创建
最后由 hamishwillee 在 10 Nov 2011 编辑
==Qt Service Framework==

Qt服务框架(Qt Service Framework )使客户端可以发现和实例化任意服务(service)。



Contents

命名空间

QtMobility API 放在 QtMobility 的命名空间. 这是为以后Mobility APIs 集成到 Qt做准备. 参考Quickstart guide中的例子体会这个命名空间如何影响使用QtMobility的开发。


简介

Qt Service Framework 定义了统一的方法 在不同的平台间查找,实现和访问各种service。

利用 服务接口(service interface),版本号(version),基于QObject的内省(introspection)这3个信息,可以兼容和访问多个平台的某个service, 使得通过Qt的客户端可以不依赖平台的调用服务。


概览

一个服务(service)是一个独立的组件提供给客户端(client)定义好的操作。客户端可以通过服务的名称,版本号和服务的对象提供的接口来查找服务。 查找到服务后,框架启动服务并返回一个指针。QServiceManager 是做这些事情的主要接口,客户端通过QserviceManager来访问服务。服务也可以像一个客户端一样向服务框架查询安装在系统上的其他服务。

服务通过插件(plug-ins)来实现。QServicePluginInterface是插件的主要接口。为了避免客户端依赖某个具体的库,服务必须继承自QObject。这样QMetaObject 系统可以用来提供动态发现和唤醒服务的能力。要使QmetaObject机制充分的工作,服务必须满足,其所有的方法都是通过 signal,slot,property 或invokable function来实现(参阅Q_INVOKABLE宏的定义)

每个服务插件只实现一个服务,但是可以为多个接口提供多个实现。这样 服务 (插件)具有一定的后向兼容性,即使 当你需要使用一个新的接口名而破坏了主要的接口服务的兼容性,已有的客户端可以继续使用之前的服务接口,新的客户端可以利用新的接口。

服务可以接受远程进程。在服务管理器上注册后 进程通过signal,slot,invokable function和property来通信,就像本地对象一样。服务可以设定为在客户端间共享,或针对一个客户端。

ServiceFrameworkArchitecture.PNG
图: 服务框架架构Service Framework Architecture

如何使用框架

这一章假设用户想访问文件存储服务(FileStorage service),这个服务提供了com.nokia.qt.examples.FileStorage接口的一个实现。service framework提供了访问这些实现的多种方法。

QServiceManager service管理器是查找和实例化service的主要类。可以通过指定service的meta data 来查找service,也可以通过默认的查找机制来查找service。

冗余查找(Verbose)

客户端代码知道service及其接口的详细信息时,可用如下代码找到service

 QServiceManager manager;
QServiceFilter filter("com.nokia.qt.examples.FileStorage");
filter.setServiceName("FileStorage");
 
// find services complying with filter
QList<QServiceInterfaceDescriptor> foundServices;
foundServices = manager.findInterfaces(filter);
Q_ASSERT(foundServices.count());
 
// instantiate the FileStorage object
QObject *fileStorage;
fileStorage = manager.loadInterface(foundServices.at(0));

默认查找

假设客户端知道用哪个接口,但是并不关心实现接口的服务是哪种类型,哪种版本。 这时可使用默认查找机制来创建service对象的实例。

 QServiceManager manager;
manager.setInterfaceDefault("FileStorageService", "com.nokia.qt.examples.FileStorage");

上面的代码 QServiceManager::setInterfaceDefault() 把FileStorageService 注册成默认实现。 当客户端调用com.nokia.qt.examples.FileStorage的实现时,FileStorageService 服务会被加载。 如果FileStorageService对同一个接口 注册了多个实现或版本,这个接口的最新版本将作为默认实现被加载。 因此接口的不同版本间需要保持二进制兼容性。

service管理器对象的当前作用域决定了 接口默认的指配是对所有用户有效还是只对当前用户有效。 用户作用域没有定义时讲使用系统默认。这使得用户可以自定义个人参数。当一个新的接口第一次安装服务时,默认设置为 system wide default selection。

基于QObject的服务

这是最常见的与servicer交互的方法。

 storage = manager.loadInterface("com.nokia.qt.examples.FileStorage");
if (storage)
QMetaObject::invokeMethod(storage, "deleteFile", Q_ARG(QString, "/tmp/readme.txt"));

上面的代码通过service的QMetaObject调用文件存储对象的deleteFile() 方法。客户端不需要知道对象的类型,因此也没有链接到具体的service库。

使用某种具体类型的服务

之前的查找机制都返回一个QObject指针。这个指针可以利用Qt的meta object系统来内省(introspect)对象。 当然在某些情况下,直接使用service对象更方便些,直接引用service的头文件,链接service提供方。这样做主要的好处是编译期检查。 缺点是客户端和service必须共享service对象的实现,链接库或者共享头文件。这样做违背了 服务框架(Service Framework )的设计原则,原意是分离客户端和服务,这样做之后两者就产生了关联,改变服务类型,需要同时改变服务和客户端。

下面的代码片段演示了大概的实现:

 #include <filestorage.h>
...
QServiceManager manager;
FileStorage *storage = 0;
...
storage = manager.loadLocalTypedInterface<FileStorage>("com.nokia.qt.examples.FileStorage");
if (storage)
storage->deleteFile("/tmp/readme.txt");

服务作用域

QServiceManager 在用户作用域或系统作用域中操作。默认在用户作用域。作用域的选择决定服务是对全局有效或只是对当前的用户有效, 查找服务和接口时是在全局服务集合中查找,还是全局服务并上当前用户的服务集合中查找。

用户作用域

在用户作用域, 服务注册在当前用户注册的存储点。当使用setInterfaceDefault()设置一个接口的默认服务时,服务可以是用户特有的或全局的服务。

查找服务和接口时,管理器会优先搜索用户特有的服务,没有找到时,在用户有权限时再查找全局的服务。

在服务被添加,删除时放送信号QServiceManager::serviceAdded()QServiceManager::serviceRemoved()。这些信号可以指定作用域参数。(只有当用户有足够的权限时才会得到全局服务的增减信号)

系统作用域

在系统作用域,服务注册在全局存储点。管理器不访问用户特定的操作。

服务和接口在全局服务中找不到就返回fail。服务只注册在全局存储点,如果使用setInterfaceDefault()注册一个用户特定的服务,则返回fail。

QServiceManager::serviceAdded() 和QServiceManager::serviceRemoved()只发送全局服务的增减信号。


增加,移除服务

服务可以在运行期安装和移除。用一个XML文件来描述服务的meta data,通过meta data的描述来链接服务的代码。

XML格式

使用XML描述的 meta data和服务的位置来安装服务,XML文件的文件类型定义如下:

 <!ELEMENT SFW ( service ) >
<!ATTLIST SFW version (1.0|1.1) #REQUIRED >
<!ELEMENT service ( name, filepath | ipcaddress, description?, interface+ ) >
<!ELEMENT description ( #CDATA ) >
<!ELEMENT filepath ( #PCDATA ) >
<!ELEMENT ipcaddress ( #PCDATA ) >
<!ELEMENT interface ( name, version, description?, capabilities?, customproperty* ) >
<!ELEMENT capabilities ( #PCDATA ) >
<!ELEMENT name ( #PCDATA ) >
<!ELEMENT version ( #PCDATA ) >
<!ELEMENT customproperty ( #CDATA ) >
<!ATTLIST customproperty key NMTOKEN #REQUIRED >

元素和属性的意义如下:

Element SubElement Description
SFW The service framework XML version tag that encapsulates the service XML. Must provide the version attribute that follows the major.minor notation and must equate to supported Qt Service Framework versions. This means the minimum version is 1.0 however users are recommended to always specify the highest available version.
service The service tag can contain an arbitrary number of interface tags and one description tag.
" description A user readable description of the purpose of the service.
" filepath 当请求某个服务要加载相应的插件时,filepath用来指定插件的绝对路径和名称。或者filepath只设定了插件名称时,会在存放库的标准路径中查找(参见QCoreApplication::libraryPaths)。注意:如果只指定插件名称, 那么名称中 要去掉文件名中平台相关的 部分,比如后缀 ".dll" 和".so",或前缀“lib”,这样才能保证跨平台的解析成功。 QLibrary负责确定插件名中平台相关的部分。
" ipcaddress 为提供远程访问的进程间服务指定 socket名或路径。必须与服务的可执行文件同名,并没有诸如“.exe”的后缀。
" name The name of the service.
interface The interface describes the properties of the interface.
" capabilities This property is a list of arbitrary strings which are interpreted as permissions/capabilities. The list elements are comma-separated and spaces after commas are not permitted. This list can be empty.
" name The name of the interface using the Java class name notation. (e.g. com.nokia.qt.TestService)
" version This property contains the interface and implementation version. The version tag follows the major.minor notation. The major version indicates the interface version the minor version the implementation version.

The version number must be greater than 1.0. The version cannot be less than 1.0 because the Service Framework is dependent on the fact that services must be binary compatible between major versions, and services with versions less than 1.0 are unlikely to be binary compatible with later versions.

" description A user readable description of the purpose of the interface.
" customproperty An implementation specific key value pair which can be used for filtering or as description.

下面是一个有效地XML服务定义文件的例子:

 <?xml version="1.0" encoding="utf-8" ?>
<SFW version="1.1">
<service>
<name>TestService</name>
<filepath>testserviceplugin</filepath>
<description>Test service description</description>
<interface>
<name>com.nokia.qt.ILocation</name>
<version>1.4</version>
<capabilities></capabilities>
<description>Interface that provides location support</description>
</interface>
<interface>
<name>com.nokia.qt.ILocation</name>
<version>1.5</version>
<capabilities></capabilities>
<description>Interface that provides location support</description>
</interface>
<interface>
<name>com.nokia.qt.ISysInfo</name>
<capabilities>ReadUserData</capabilities>
<version>2.3</version>
<description>Interface that provides system information support</description>
<customproperty key="key1">value1</customproperty>
<customproperty key="key2">value2</customproperty>
</interface>
</service>
</SFW>

生成XML的工具

tools/servicexmlgen目录下有一个生成和查看XML服务文件的GUI工具。这个工具可以方便的输入服务的metadata和接口细节来生成插件或进程间通信的服务所对应的XML。也可以用这个工具来查看以后的XML服务文件。

注意这个工具会默认用已知的Qt Service Framework的最高版本 来设置XML版本属性,对读取的文件也会强制设定这个版本属性。 下面的截图是工具加载上面TestService XML文件的效果。

Servicexmlgen.png


动态加载服务

可使用QServiceManager::addService() 和QServiceManager::removeService()在任何时候增减服务。

在Symbian设备上服务可以通过Symbian安装包安装。详细操作参阅Qt Service Framework on Symbian


标识服务

服务的每个实现由服务名,接口名和版本号来标识。这些信息包装在QServiceInterfaceDescriptor中,用QServiceInterfaceDescriptor来检索服务的对象。 QServiceFilter可方便查找已安装的服务。开发者可以指定meta data的查找条件。下面的例子演示了 QServiceInterfaceDescriptor 和 QServiceFilter相互作用, 例子将所有实现了com.nokia.qt.ILocation接口的服务索引出来。

 QServiceManager mgr;
QServiceFilter filter;
filter.setInterface("com.nokia.qt.ILocation");
QList<QServiceInterfaceDescriptor> list = mgr.findInterfaces(filter);
for(int i = 0; i < list.size(); i++) {
QObject *serviceObject;
serviceObject = mgr.loadInterface(list[i]);
 
// returned object owned by client
if (serviceObject)
serviceObject->setParent(this);
}

升级服务

有两种方法升级一个服务。第一种是增量的方法,使用QServiceManager::addService() 注册一个XML服务描述文件,服务名已经存在,而XML服务文件定义了新的接口实现。举个例子:一个已有的服务”ovi“可能定义了接口"IDownload"的1.0版。这时QServiceManager::addService()读取一个XML描述文件,这个文件定义了”ovi“服务其中接口"IDownload"的实现了1.1版,两个版本的实现都可以被使用。 第二种是替换的方法,这时旧的服务被新的服务完全替代。举个例子:”ovi“服务其中接口"IDownload"的实现了1.0版,新的”ovi“服务其中接口"IDownload"的实现了1.0版和1.1版,这时旧的服务必须先用QServiceManager::removeService()移除,新的服务才能安装上。



进程外的服务

Qt 服务框架为进程外的服务提供了支持机制,使得客户端可以远程加载接口。这些进程间的部署类似于本地的服务,使用XML描述文件中的IPC(进程间通信)地址标签,调用 QRemoteServiceRegister::publishEntries()将服务增加到远程的服务管理器中。客户端通过metaobject访问服务,跟同进程的服务很类似。远程服务用QRemoteServiceRegister::Entry来表示。例子 Echo Client演示了如何实例化合使用远程服务。

跨进程的服务使用平台上可用的各种 跨进程机制, 于是继承这些机制的局限性。 在Symbian上使用Symbian的Client-Server的IPC机制,在QtDBus有效的地方使用QtDBus做跨进程,其他时候退化成使用QLocalSocket 做进程间通信。

ServiceFrameworkIPC.PNG
图: 服务框架进程间通信 Service Framework IPC

D-Bus 服务

Qt服务框架的 架构中使用QtDBus 组件来提供进程间通信的机制。 QtDBus 组件提供了访问signals, slots, invokable methods 和properties的方法。

限制

用QtDBus做IPC一个已知的限制是,不能读取枚举值,这是因为对D-Bus的支持没跟上。下一版QtDBus组件应该会增加对枚举的支持。一个应对方案是 手动定义并使用整数 来代替枚举 如Declarative Serviceframework Dialer例子中做的那样。

此外,由于跨进程的特性,对象的指针不能在服务和客户端间传递,因此遗憾的是,这意味着 typedef macro中的内容不能在进程间有效地传递。

服务自动启动

D-Bus服务很容易自动启动。将.service文件放在D-Bus 的守护(daemon)搜索路径下就行, 使用servicesw 工具提供了一个选项 在用户本地D-Bus文件夹中生成需要的文件,这样就不用自己手动做了。 当客户端请求一个服务,服务没启动的时会自动启动。所有连接在服务signal上的slot断开后,服务会自动关闭。 运行下面地 命令来对接D-Bus的跨进程机制。

  ./servicefw dbusservice <service-xml-file> <service-file>

.service文件通常生成在$home/.local/share/dbus-1/services文件夹中,除非$$XDG_DATA_HOME环境变量设为其他值。如果使用了--system选项,.service文件会生成在/usr/share/dbus-1/services, 前提是用户的有足够的权限。

注意,要使自动启动正常工作,服务需要预注册,或运行时注册,这样客户端才能通过QServiceManager 查找到跨进程的接口。通常在部署的时候用servicefw工具部署服务就可注册了。


Symbian Client-Server 框架

限制

对象的指针,typedef包装的东西都不能在服务和客户端之间传递。symbian上使用的IPC 信息要尽可能的小。symbian的Client-Server IPC授权一个固定的buffer大小,目前是255 byte。大的消息会导致很多context的交换。很多大消息的频繁传递会导致系统性能降低。

服务自动启动

客户端调用loadInterface时Symbian IPC会自动启动Server进程。可执行程序的名字必须与XML注册文件中<ipcaddress>标签中的名字一样。例如 <ipcaddress>xyzabc</ipcaddress>意味着服务调用xyzabc.exe。

服务注册

为使客户端能定位和使用到服务,所有服务必须为客户端注册。Symbian提供3种注册方法。

   * 自动注册
   * 安全注册
   * ROM编译注册
Symbian自动注册

服务不是Symbian Signed签名的且不需要很高安全性时 优先使用自动注册。XML拷贝到IPC后端的导入目录,在Qt工程文件pro 中可以这样写:

 xmlautoimport.path = /private/2002AC7F/import/
xmlautoimport.sources = <service xml file>.xml
DEPLOYMENT += xmlautoimport

或在 symbian .pkg 文件中这样写:

 "/epoc32/data/z/private/2002AC7F/import/<service xml file>.xml" - "c:\private\2002AC7F\import\<service xml file>.xml"
Symbian安全注册

对于经Symbian Signed签名需要较高安全性的服务可以使用后安装程序(post install)。注册时使用VID/SID来检查服务的真实性。如果安装者定义了VID,将VID保存在注册数据库中,以后只有使用这个更VID才能移除或重装这个服务。如果没有定义VID,就使用SID.VID或SID保留在注册数据库中,直到解除注册操作。只有使用同样的VID/SID才能注册同名的服务。 examples/serviceinstaller_sfw_symbian中有个关于这件事的例子。 关于VID和SID的更多信息参阅:

  • VendorID
  • SecureID
ROM编译注册

对编译到rom image中的Symbian应用,注册必须在编译期使用 host工具 servicdbgen来完成。运行命令:

  servicedbgen -tall add <service xml file>

需要相应的 iby文件,和一些设置,使得服务的可执行文件和相关的文件包括到rom image中。

服务安全过滤器

在初始化连接时,服务可以安装一个过滤器来检查客户端的连接,如果需要可中断连接。过滤器是这样的方法:

 bool securityFilter(const void *p)
{
const RMessage2 *aMessage = reinterpret_cast<const RMessage2*>(p);
if(check_security) {
return true; // accept connection
}
else {
retun false; // reject connection
}
}

给Client-Server的安全检查返回CPolicyServer::EPass 如果上面的函数返回true,返回 CPolicyServer::EFail 如果上面的函数返回false。QRemoteServiceRegister::setSecurityFilter()方法设置过滤器。 Symbian Policy Server API 与RMessage2可以导出security policy的细节。如RMessage2::HasCapability ,RMessage2::VendorId()。

错误处理

跨进程错误会发生。 由服务管理器返回的QObject会增加一个信号 ,errorUnrecoverableIPCFault(QService::UnrecoverableIPCError) ,客户端必须连接这个信号。错误发生后对象必须被销毁,不能再有其他调用。错误的后续恢复 由客户端应用处理。


Local Socket

在没有D-Bus或Symbian Client-Server框架的平台上,进程间通信机制由 基于本地的socket的协议来实现。

限制

与其他IPC类似,对象指针,typdef在local socket上不工作。Local socket 要求服务的可执行文件在PATH环境变量设定的目录下,如此自动启动才能工作。有的平台不支持所有的域,对不支持的QRemoteServiceResgiterCredentials 返回-1。

服务自动启动

<ipcaddress>标签设置成可执行文件的文件名。例如在windows操作系统上 <ipcaddress>xyzabc</ipcaddress> 对应 xyzabc或xyzabc.exe。在PATH环境变量的目录中搜索可执行文件。可执行文件没找到,或服务没有启动,客户端会收到错误报告。

服务注册

服务必须注册,使用 'servicefw' 来注册XML文件。 服务使用前必须运行这个工具,例如:

  servicefw add <service xml file>

服务安全过滤器

服务可以安装安全过滤器来拒绝一些客户端的连接请求

 bool securityFilter(const void *p)
{
const QRemoteServiceRegisterCredentials *cr = (const struct QRemoteServiceRegisterCredentials *)p;
if(cr->uid == 0) {
return true; // accept connection
}
else {
retun false; // reject connection
}
}
 
struct QRemoteServiceRegisterCredentials {
int fd;
int pid;
int uid;
int gid;
};

返回true接受连接,返回false终止连接。有的平台不提东所有的结果,未定义的值设为-1。使用方法 QRemoteServiceRegister::setSecurityFilter()设置过滤器, 一次只能设置一个过滤器。

错误处理

进程外服务和本地服务表现相似,有一个例外。可能有IPC错误,或远程服务意外关闭。服务管理器为 进程外服务创建的QObject增加了一个信号。errorUnrecoverableIPCFault(QService::UnrecoverableIPCError) ,客户端必须连接这个信号。错误发生时对象需要销毁,不能有进一步的其他调用,错误恢复由客户端应用处理。


主要的类

QAbstractSecuritySession Generic mechanism to enable permission checks for services
QRemoteServiceRegister Manages instances of remote service objects
QService Contains miscellaneous identifiers used throughout the Qt Service framework library
QServiceContext Context information to services
QServiceFilter Defines criteria for defining a sub-set of all available services
QServiceInterfaceDescriptor Identifies a service implementation
QServiceManager Enables the loading of service plugins and the (de)registration of services
QServicePluginInterface Defines the interface that every plug-in based service must implement

QML 相关类

Qt 服务框架提供通过描述性的UI发现和选择服务的功能,提供服务和服务列表 的QML元素(element)。详细信息参阅QML Service Framework Plugin

http://doc.qt.nokia.com/qtmobility-1.1.0/qml-service.html QML Service Element The Service element holds an instance of a service object.
http://doc.qt.nokia.com/qtmobility-1.1.0/qml-servicelist.html QML ServiceList Element The ServiceList element holds a list of Service elements.

例子

相关链接

711 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×