一、前言
本文分析的相关代码实现位于distributedschedule_samgr_lite\interfaces\kits\communication\broadcast\broadcast_interface.h
和distributedschedule_samgr_lite\communication\broadcast\source\pub_sub_implement.c
。 在本系列中将对以下数据结构及函数进行详解。
struct Consumer:消息的接收者
struct Provider:消息的发布者
struct Subscriber:消息的订阅管理者
struct PubSubInterface:发布和订阅的接口
struct PubSubImplement:广播子功能实例
BCE_CreateInstance:创建广播子功能实例
AddTopic:添加主题信息
Subscribe:订阅主题
ModifyConsumer:更改主题的消费者
Unsubscribe:取消主题的订阅
Publish:发布消息
ImmediatelyPublish:立即发布
DefaultHandle:消息处理函数
二、宏定义及数据结构
//当前服务名称
#define BROADCAST_SERVICE "Broadcast"
//当前功能名称
#define PUB_SUB_FEATURE "Provider and subscriber"
//表示事件或数据的主题,用于区分不同类型的事件或数据
typedef uint32 Topic;
//枚举广播服务特有的错误代码
enum BroadcastErrCode {
//当前topic已被订阅,即重复订阅
EC_ALREADY_SUBSCRIBED = EC_SUCCESS + 1
};
//定义topic的消费者,用于接收事件和推送数据
typedef struct Consumer Consumer;
struct Consumer {
//消费者的身份标识
const Identity *identity;
/*
@consumer:数据的消费者
@topic:将被处理的topic
@origin:指向要处理的数据的指针
定义消费者如何处理已发布的topic中的事件或数据。
*/
void (*Notify)(Consumer *consumer, const Topic *topic, const Request *origin);
//检查两个消费者是否相等,防止重复订阅
BOOL (*Equal)(const Consumer *current, const Consumer *other);
};
// 定义topic的事件和数据的提供者,负责将消息发布到指定的topic中
typedef struct Provider Provider;
struct Provider {
//向指定topic发布事件或数据。
BOOL (*Publish)(IUnknown *iUnknown, const Topic *topic, uint8 *data, int16 len);
};
//定义外部接口的订阅者,以订阅主题的事件和数据。
typedef struct Subscriber Subscriber;
//订阅器负责消费者和topic之间的交互以及topic到broadcast的交互
//即负责将topic关联到broadcast中,将消费者关联到topic上
//修改topic的使用者,为消费者退订topic
struct Subscriber {
//将指定的主题添加到Broadcast服务
int (*AddTopic)(IUnknown *iUnknown, const Topic *topic);
//为consumers订阅指定的主题。
int (*Subscribe)(IUnknown *iUnknown, const Topic *topic, Consumer *consumer);
//修改指定主题的使用者。
Consumer *(*ModifyConsumer)(IUnknown *iUnknown, const Topic *topic, Consumer *old, Consumer *current);
//退订指定主题。
Consumer *(*Unsubscribe)(IUnknown *iUnknown, const Topic *topic, const Consumer *consumer);
};
typedef struct PubSubInterface PubSubInterface;
struct PubSubInterface {
INHERIT_IUNKNOWN; //继承iunknown
Subscriber subscriber; //订阅者
Provider provider; //发布者
};
//消息类型
enum MessageID {
MSG_PUBLISH = 1,
MSG_BUTT
};
//继承,并封装PubSubFeature,是广播功能的实例对象
typedef struct PubSubImplement {
INHERIT_IUNKNOWNENTRY(PubSubInterface);//继承接口
PubSubFeature *feature; //功能对象成员
} PubSubImplement;
//广播服务子功能的全局实例
static PubSubImplement g_pubSubImplement = {
DEFAULT_IUNKNOWN_ENTRY_BEGIN,
.subscriber.AddTopic = AddTopic,
.subscriber.Subscribe = Subscribe,
.subscriber.ModifyConsumer = ModifyConsumer,
.subscriber.Unsubscribe = Unsubscribe,
.provider.Publish = Publish,
DEFAULT_IUNKNOWN_ENTRY_END,
.feature = NULL
};
三、函数实现详解
创建广播功能实例
//根据feature创建实例
PubSubImplement *BCE_CreateInstance(Feature *feature)
{
//为PubSubFeature配置实例
g_pubSubImplement.feature = (PubSubFeature *)feature;
return &g_pubSubImplement;
}
为广播功能添加topic
/*
函数功能:添加topic
函数参数:@iUnknown:接口对象
@topic:待添加的topic
函数返回:添加成功 返回EC_SUCCESS,添加失败 返回EC_INVALID or EC_FAILURE or EC_NOMEMORY
函数描述:topic的信息是通过relation来维护的。所以添加topic本质上就是在PubSubFeature功能中添加新的关系,关系包含了topic信息和消费者。
将topic保存到新的关系结点中,然后将它以头插法的方式插入到链表中
*/
static int AddTopic(IUnknown *iUnknown, const Topic *topic)
{
//参数检查
if (iUnknown == NULL || topic == NULL) {
return EC_INVALID;
}
//获取接口的PubSubImplement成员
PubSubImplement *broadcast = GET_OBJECT(iUnknown, PubSubImplement, iUnknown);
if (broadcast->feature == NULL || broadcast->feature->GetRelation == NULL) {
//实例对象未初始化,返回EC_FAILURE
return EC_FAILURE;
}
//查询feature与指定topic的关系
if (broadcast->feature->GetRelation(broadcast->feature, topic) != NULL) {
//关系已存在,无需添加,则返回EC_FAILURE
return EC_FAILURE;
}
//关系不存在,添加topic
//获取指定功能下的关系链表的头节点
Relation *head = &broadcast->feature->relations;
//构建新的关系结点
Relation *newRelation = (Relation *)SAMGR_Malloc(sizeof(Relation));
if (newRelation == NULL) {
//内存分配失败
return EC_NOMEMORY;
}
//初始化relation
newRelation->topic = *topic; //关联topic
newRelation->callbacks.consumer = NULL; //初始化消费者结点
//链表结点指针初始化
UtilsListInit(&newRelation->callbacks.node);
MUTEX_Lock(broadcast->feature->mutex);
//将新的关系结点插入双向链表,头插法
UtilsListAdd(&head->node, &(newRelation->node));
MUTEX_Unlock(broadcast->feature->mutex);
return EC_SUCCESS;
}
订阅topic
/*
函数功能:为消费者订阅指定的topic
函数参数:@iUnknown:统一的接口对象,屏蔽对象细节,通过GET_OBJECT获取真正的对象
@topic:待订阅的topic
@consumer:topic中数据的消费者
函数返回:订阅成功 返回EC_SUCCESS,订阅失败 返回EC_INVALID or EC_FAILURE or EC_ALREADY_SUBSCRIBED or EC_NOMEMORY
函数描述:在指定的功能下创建订阅关系。在订阅前先判断topic是否有效以及是否重复订阅。
若满足订阅条件,则创建新的消费者结点,保存消费者信息,然后将该结点插入到与topic关联的消费者链表中。
*/
static int Subscribe(IUnknown *iUnknown, const Topic *topic, Consumer *consumer)
{
//参数检查
if (iUnknown == NULL || topic == NULL || consumer == NULL) {
return EC_INVALID;
}
//获取PubSubImplement成员
PubSubImplement *broadcast = GET_OBJECT(iUnknown, PubSubImplement, iUnknown);
if (broadcast->feature == NULL || broadcast->feature->GetRelation == NULL) {
//未初始化,返回EC_FAILURE
return EC_FAILURE;
}
//在创建订阅关系前,先查询指定的topic是否存在
Relation *relation = broadcast->feature->GetRelation(broadcast->feature, topic);
if (relation == NULL) {
//topic不存在,返回EC_FAILURE
return EC_FAILURE;
}
//链表中存在包含当前topic的relation,添加消费者
MUTEX_Lock(broadcast->feature->mutex);
ConsumerNode *item = NULL;
//遍历关系链表
UTILS_DL_LIST_FOR_EACH_ENTRY(item, &relation->callbacks.node, ConsumerNode, node) {
//遍历链表,检查当前消费者是否已订阅该topic
if (item->consumer->Equal(item->consumer, consumer)) {
//已订阅,返回EC_ALREADY_SUBSCRIBED,不允许重复订阅
MUTEX_Unlock(broadcast->feature->mutex);
return EC_ALREADY_SUBSCRIBED;
}
}
MUTEX_Unlock(broadcast->feature->mutex);//这里解锁,线程安全?疑惑
//当前消费者未订阅该topic,建立订阅关系
//创建消费者结点
ConsumerNode *consumerNode = (ConsumerNode *)SAMGR_Malloc(sizeof(ConsumerNode));
if (consumerNode == NULL) {
return EC_NOMEMORY;
}
//链表结点指针初始化
UtilsListInit(&consumerNode->node);
//更新消费者信息
consumerNode->consumer = consumer;
MUTEX_Lock(broadcast->feature->mutex);
//获取存储消费者的链表
ConsumerNode *head = &relation->callbacks;
//将关系结点插入双向链表,头插法
UtilsListAdd(&head->node, &consumerNode->node);
MUTEX_Unlock(broadcast->feature->mutex);
return EC_SUCCESS;
}
总是有很多小伙伴反馈说:OpenHarmony开发不知道学习哪些技术?不知道需要重点掌握哪些OpenHarmony开发知识点? 为了解决大家这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习路线与学习文档给大家用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技术知识点。
OpenHarmony 开发环境搭建:https://gitcode.com/HarmonyOS_MN/733GH/overview
《OpenHarmony源码解析》
搭建开发环境
系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……