1 前言
在鸿蒙L0开发时,发现里面经常会用到samgr,用于各个模块之间的通讯。
这里,最重要的就是一个组件化的概念,所有的模块,都是service,注册到samgr中,供其他模块使用。
同时,也可以调用samgr,获取其他模块的接口,并进行调用。
这里分析的是基于L0的samgr_lite,因为这个实现比较简单,它本身是单进程的系统,不需要涉及跨进行的消息交互。
但同时,这个samgr也不简单,它已超越了组件管理的概念,加入了不一样的东西。
- 服务注册与查询;
- 服务的子服务(Feature)的注册与查询;
- 服务的线程管理和消息队列;
- 消息总线机制;
2 总体框架
服务中心是一个单例模式,所有服务在开机时注册到服务中心。
开机以后,各个客户查询并使用服务。
服务中心的组成如下
服务中心里,通过vector保存了多个服务;
每一个服务对应一个线程池和消息队列;
每一个服务又对应0个或多个feature;
3 服务
3.1 服务接口
服务的接口如下:
foundation/distributedschedule/samgr_lite/interfaces/kits/samgr/service.h
const char *(*GetName)(Service * service);
BOOL (*Initialize)(Service * service, Identity identity);
BOOL (*MessageHandle)(Service * service, Request * request);
TaskConfig (*GetTaskConfig)(Service * service)
3.2 服务实现样例
代码路径applications/sample/wifi-iot/app/samgr/service_example.c
typedef struct DefaultFeatureApi {
INHERIT_IUNKNOWN;
void (*SyncCall)(IUnknown *iUnknown);
} DefaultFeatureApi;
typedef struct ExampleService {
INHERIT_SERVICE;
INHERIT_IUNKNOWNENTRY(DefaultFeatureApi);
Identity identity;
} ExampleService;
展开后其实是这样的:
typedef struct DefaultFeatureApi {
int (*QueryInterface)(IUnknown *iUnknown, int version, void **target);
int (*AddRef)(IUnknown *iUnknown);
int (*Release)(IUnknown *iUnknown);
void (*SyncCall)(IUnknown *iUnknown); // 服务自定义的特殊函数
} DefaultFeatureApi;
typedef struct ExampleService {
// 下面4个是服务接口
const char *(*GetName)(Service * service);
BOOL (*Initialize)(Service * service, Identity identity);
BOOL (*MessageHandle)(Service * service, Request * request);
TaskConfig (*GetTaskConfig)(Service * service);
// 这二个是iknow用到的标识,实际上没什么用
uint16 ver;
int16 ref;
DefaultFeatureApi iUnknow;
Identity identity;
} ExampleService;
创建实例
static ExampleService g_example = {
.GetName = GetName,
.Initialize = Initialize,
.MessageHandle = MessageHandle,
.GetTaskConfig = GetTaskConfig,
DEFAULT_IUNKNOWN_ENTRY_BEGIN,
.SyncCall = SyncCall,
DEFAULT_IUNKNOWN_ENTRY_END,
};
展开后为:
static ExampleService g_example = {
.GetName = GetName,
.Initialize = Initialize,
.MessageHandle = MessageHandle,
.GetTaskConfig = GetTaskConfig,
.ver = 0x20,
.ref = 1,
.iUnknown = {
.QueryInterface = IUNKNOWN_QueryInterface,
.AddRef = IUNKNOWN_AddRef,
.Release = IUNKNOWN_Release,
. SyncCall = SyncCall, // 组件自定义函数的实现指针
},
3.3 神奇的iUnknow
iUnkow这个东东,发现到处都有它的影子, windows, linux , android。 但如果说解释得最清楚,非潘爱民的《com原理与应用》 这本书莫属。
3.3.1 引用计数
AddRef和Release比较好理解, 本质上就是引用计数,c++和java都有这个概念,用于做内存管理,当计数器为0时才会真正释放对象。
但QueryInterface,如果单从鸿蒙的代码看,是看出不内容的。 因为鸿蒙虽然用了这个接口,但实际上返回的都是一样的作用。
3.3.2 查询接口QueryInterface
QueryInterface的作用,一般是用于如果组件比较复杂,可能有有多个接口,则可以通过唯一标识,查询对应的接口。如下面的字典对象,如果邮件客户端在使用时,有可能就只用语法检查的能力,这样邮件客户端使用过程如下:
- 首先从系统得到IUnknown接口;
- 调用QueryInterface,传入语法检查的标识uuid,得到ISpellCheck的接口集;
- 在用户输入文字时,调用ISpellCheck接口集中的函数进行拼写检查;
鸿蒙并没有让组件自己管理自己的多个Interface(子能力或Feature),因此这个接口严重退化到,只返回自身。
鸿蒙samgr在实现时,把子服务的管理,放到samgr中, 参见服务中心的组成。这样做法,简化了各个服务及Feature的开发,但确实与QueryInterface的概念有冲突。