×
Namespaces

Variants
Actions
(Difference between revisions)

Qt Service Framework 文档

From Nokia Developer Wiki
Jump to: navigation, search
flycarl (Talk | contribs)
flycarl (Talk | contribs)
Line 228: Line 228:
 
下面的截图是工具加载上面TestService XML文件的效果。
 
下面的截图是工具加载上面TestService XML文件的效果。
  
 +
[[Image:servicexmlgen.png|600px]]
  
 +
----
 +
 +
=='''动态加载服务'''==
 +
可使用QServiceManager::addService() 和QServiceManager::removeService()在任何时候增减服务。
 +
 +
在Symbian设备上服务可以通过Symbian安装包安装。详细操作参阅[http://doc.qt.nokia.com/qtmobility-1.1.0/service-framework-on-symbian.html Qt Service Framework on Symbian]
 +
 +
----
 +
 +
=='''标识服务'''==
 +
服务的每个实现由服务名,接口名和版本号来标识。这些信息包装在[http://doc.qt.nokia.com/qtmobility-1.1.0/qserviceinterfacedescriptor.html QServiceInterfaceDescriptor]中,用QServiceInterfaceDescriptor来检索服务的对象。
 +
[http://doc.qt.nokia.com/qtmobility-1.1.0/qservicefilter.html QServiceFilter]可方便查找已安装的服务。开发者可以指定meta data的查找条件。下面的例子演示了  QServiceInterfaceDescriptor 和 QServiceFilter相互作用, 例子将所有实现了com.nokia.qt.ILocation接口的服务索引出来。
 +
<code cpp>
 +
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);
 +
}
 +
</code>
 +
 +
----
 +
 +
=='''升级服务'''==
 +
 +
有两种方法升级一个服务。第一种是增量的方法,使用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(进程间通信)地址标签,调用 [http://doc.qt.nokia.com/qtmobility-1.1.0/qremoteserviceregister.html#publishEntries QRemoteServiceRegister::publishEntries()]将服务增加到远程的服务管理器中。客户端通过metaobject访问服务,跟同进程的服务很类似。远程服务用[http://doc.qt.nokia.com/qtmobility-1.1.0/qremoteserviceregister-entry.html QRemoteServiceRegister::Entry]来表示。例子 [http://doc.qt.nokia.com/qtmobility-1.1.0/sfwecho.html Echo Client]演示了如何实例化合使用远程服务。
 +
 +
跨进程的服务使用平台上可用的各种 跨进程机制, 于是继承这些机制的局限性。 在Symbian上使用Symbian的Client-Server的IPC机制,在[http://qt.nokia.com/doc/4.7/qtdbus.html QtDBus]有效的地方使用QtDBus做跨进程,其他时候退化成使用[http://qt.nokia.com/doc/4.7/qlocalsocket.html QLocalSocket ]做进程间通信。
 +
==='''D-Bus 服务'''===
 +
Qt服务框架的 架构中使用QtDBus 组件来提供进程间通信的机制。 QtDBus 组件提供了访问signals, slots, invokable methods 和properties的方法。
 +
====限制====
 +
用QtDBus做IPC一个已知的限制是,不能读取枚举值,这是因为对D-Bus的支持没跟上。下一版QtDBus组件应该会增加对枚举的支持。一个应对方案是 手动定义并使用整数 来代替枚举 如[http://doc.qt.nokia.com/qtmobility-1.1.0/declarative-sfw-dialer.html Declarative Serviceframework Dialer]例子中做的那样。
 +
 +
此外,由于跨进程的特性,对象的指针不能在服务和客户端间传递,
 +
A current known limitation for Qt Service Framework using QtDBus for IPC is that enumerators cannot be read due to lack of support on D-Bus. This will be added in future versions once the QtDBus module enables enumerator support. A current known workaround is to manually define and use integers as demonstrated in the Declarative Serviceframework Dialer example. Furthermore, due to the nature of IPC, pointers to objects cannot be passed between services and clients. Unfortunately, this also means that anything wrapped in a typedef macro cannot be communicated effectively between processes.
 +
Service Autostart
 +
 +
D-Bus services inherit automatic start support by providing .service files to the D-Bus daemon search paths. Aside from producing these files manually, the servicefw tool provides an option to generate the corresponding files inside the local user D-Bus folder for convenience. This will allow all client requests to automatically startup the service if it isn't already running. Consequently, once every client has closed its service instance the service will be automatically terminated after invoking any slots connected to notification signals.
 +
 +
Running the following command will enable rendezvous for D-Bus IPC.
 +
 +
./servicefw dbusservice <service-xml-file> <service-file>
 +
 +
The .service files will typically be generated in the $home/.local/share/dbus-1/services directory unless the $$XDG_DATA_HOME environment variable is set. If the --system option is used the tool will generate the file inside /usr/share/dbus-1/services if the user permissions are met.
 +
 +
Note that in order for the autostart feature to function correctly, the service needs to be either pre-registered or registered at run-time so that clients can use a QServiceManager to discover the IPC interfaces that invoke the D-Bus service files. This is usually done on deployment by adding services through the servicefw tool.
  
 
==<u>相关链接</u>==
 
==<u>相关链接</u>==

Revision as of 13:26, 9 January 2011

Contents

Qt Service Framework

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



命名空间

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


简介

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

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


概览

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

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

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

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


如何使用框架

这一章假设用户想访问文件存储服务(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 The absolute path and name of the plug-in to be loaded when this service is requested. Alternatively if the plug-in name only is provided the standard library paths (see QCoreApplication::libraryPaths()) are used to find the plug-in. Note that if the plugin name is given only, platform specific parts such as the suffix ".dll" and ".so" or plugin prefix "lib" should be removed to enable cross platform resolution. QLibrary is used to determine the platform specific parts of the plugin.
" ipcaddress The socket name or path that the inter-process service will be published on that will provide clients with remote access. Should be the same name as the executable for the service without any suffix such as ".exe" etc.
" 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 做进程间通信。

D-Bus 服务

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

限制

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

此外,由于跨进程的特性,对象的指针不能在服务和客户端间传递, A current known limitation for Qt Service Framework using QtDBus for IPC is that enumerators cannot be read due to lack of support on D-Bus. This will be added in future versions once the QtDBus module enables enumerator support. A current known workaround is to manually define and use integers as demonstrated in the Declarative Serviceframework Dialer example. Furthermore, due to the nature of IPC, pointers to objects cannot be passed between services and clients. Unfortunately, this also means that anything wrapped in a typedef macro cannot be communicated effectively between processes. Service Autostart

D-Bus services inherit automatic start support by providing .service files to the D-Bus daemon search paths. Aside from producing these files manually, the servicefw tool provides an option to generate the corresponding files inside the local user D-Bus folder for convenience. This will allow all client requests to automatically startup the service if it isn't already running. Consequently, once every client has closed its service instance the service will be automatically terminated after invoking any slots connected to notification signals.

Running the following command will enable rendezvous for D-Bus IPC.

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

The .service files will typically be generated in the $home/.local/share/dbus-1/services directory unless the $$XDG_DATA_HOME environment variable is set. If the --system option is used the tool will generate the file inside /usr/share/dbus-1/services if the user permissions are met.

Note that in order for the autostart feature to function correctly, the service needs to be either pre-registered or registered at run-time so that clients can use a QServiceManager to discover the IPC interfaces that invoke the D-Bus service files. This is usually done on deployment by adding services through the servicefw tool.

相关链接

551 page views in the last 30 days.
×