超精简的订阅发布事件组件--SPEvent

描述

概述

  • 本文主要描述一个超精简的订阅发布事件组件--SPEvent。
  • 在实际开发过程中,一个事件的产生会产生很多业务的执行,或者多个事件都要执行同一个业务的执行。在这种场景下有两种做法:
  1. 将同一个事件的业务放在一个函数中,然后事件产生的时候执行对应的函数。
  2. 某个业务需要哪个事件,它自己监听对应事件并执行。
  • 显然,第一种策略会将业务与业务之间耦合在一起,对后期维护是非常痛苦的;第二种显然会更加有优势,不同业务完全解耦,独立完成事件的业务。
  • 第二种策略的方式,实际在软件架构中经常看到,比如MQTT的通信(通过订阅对应的topic去监听对应内容)。
  • 有了上述的需求,作者做了一个超精简的订阅发布事件组件。整个逻辑很简单。

超精简的SPEvent组件,实现方法

  1. 整个订阅发布事件机制,引入两个东西:EventHub和EventNode。

  • EventHub:每一个事件类型都为一个EventHub,然后挂在HubList中。
  • EventNode:每一个订阅事件的业务为一个EventNode,然后挂在对应的EventHub中。
  • 整个订阅发布事件机制围绕着EventHub和EventNode,特点:

  • 资源占用极小,接口操作简单
  • 事件支持动态订阅,动态注销。
  • SPEvent采用双向链表进行维护整个订阅-发布逻辑

  • SPEvent一定存在一个EventHubList链表来维护事件类型,它默认是没有任何EventHub节点,
  • 订阅事件流程:当订阅者订阅事件之后,如果事件不存在,则申请一个EventHub,并将EventHub挂在到EventHubList链表中;然后申请一个EventNode,及将对应EventNode挂在EventNodeList链表。
  • 发布事件流程:当发布者发布事件时,会从EventHubList中查询有没有对应的EventHub,如果EventHub存在,则将事件消息发布给对应EventHub下所有EventNode。
  • 注销事件订阅流程:当订阅者注销已经订阅的事件,会从EventHubList中查询有没有对应的EventHub,如果EventHub存在,则将对应EventNode从EventHub中删除。

组件

超精简的SPEvent组件,接口说明:

函数 说明
SPEventInit 初始化函数
SPEventDeinit 去初始化函数
SPEventSubscribe 订阅事件函数
SPEventUnsubscribe 注销订阅事件函数
SPEventPublish 发布事件消息
SPEventClear 清除事件池
RecvtInfoDump 导出事件池信息

超精简的SPEvent组件,代码实现

  1. 整个代码接口存在3个文件:spevent.c、spevent.h、spevent_def.h。其中:
  2. spevent_def.h文件说明:定义了屏蔽平台相关接口的宏和定义了双向量表操作的宏定义。双向量表在SPEvent中式至关重要数据结构:
#ifndef __SPEVENT_DEF_H__
#define __SPEVENT_DEF_H__



#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SPEVENT_INLINE                           static __inline
#define SPEVENT_EVENT_NAME_LEN                   16

#ifndef SPEVENT_MALLOC
    #define SPEVENT_MALLOC                       malloc
#endif

#ifndef SPEVENT_FREE
    #define SPEVENT_FREE                         free
#endif

#ifndef SPEVENT_PRINT
    #define SPEVENT_PRINT                        printf
#endif

struct SPEventListNode
{
    struct SPEventListNode *next;
    struct SPEventListNode *prev;
};
typedef struct SPEventListNode SPEventList;

SPEVENT_INLINE void SPEVENT_LIST_INIT(SPEventList *l)
{
    l->next = l->prev = l;
}

SPEVENT_INLINE void SPEVENT_LIST_INSERT_AFTER(SPEventList *l, SPEventList *n)
{
    l->next->prev = n;
    n->next = l->next;
    l->next = n;
    n->prev = l;
}

SPEVENT_INLINE void SPEVENT_LIST_INSERT_BEFORE(SPEventList *l, SPEventList *n)
{
    l->prev->next = n;
    n->prev = l->prev;
    l->prev = n;
    n->next = l;
}

SPEVENT_INLINE void SPEVENT_LIST_REMOVE(SPEventList *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;
    n->next = n->prev = n;
}

SPEVENT_INLINE int SPEVENT_LIST_LEN(const SPEventList *l)
{
    int len = 0;
    const SPEventList *p = l;
    while (p->next != l) {
        p = p->next;
        len ++;
    }
    return len;
}

#define SPEVENT_CONTAINER_OF(ptr, type, member)                                  
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

#define SPEVENT_LIST_OBJ_INIT(obj) {&(obj), &(obj)}

#define SPEVENT_LIST_ENTRY(node, type, member)                                   
    SPEVENT_CONTAINER_OF(node, type, member)

#define SPEVENT_LIST_FOR_EACH(pos, head)                                         
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define SPEVENT_LIST_FOR_EACH_SAFE(pos, n, head)                                 
    for (pos = (head)->next, n = pos->next; pos != (head);                       
        pos = n, n = pos->next)

#define SPEVENT_LIST_FOR_EACH_ENTRY(pos, head, member)                           
    for (pos = SPEVENT_LIST_ENTRY((head)->next, typeof(*pos), member);           
         &pos->member != (head);                                                 
         pos = SPEVENT_LIST_ENTRY(pos->member.next, typeof(*pos), member))

#define SPEVENT_LIST_FOR_EACH_ENTRY_SAFE(pos, n, head, member)                   
    for (pos = SPEVENT_LIST_ENTRY((head)->next, typeof(*pos), member),           
         n = SPEVENT_LIST_ENTRY(pos->member.next, typeof(*pos), member);         
         &pos->member != (head);                                                 
         pos = n, n = SPEVENT_LIST_ENTRY(n->member.next, typeof(*n), member))

#define SPEVENT_LIST_FIRST_ENTRY(ptr, type, member)                              
    SPEVENT_LIST_ENTRY((ptr)->next, type, member)

#endif
  1. spevent.c文件说明:SPEvent的接口实现;整个逻辑通过链表的嵌套,实现了事件的管理,事件的订阅,事件的发布。
#include "spevent.h"

static SPEventList g_hublist = {0};

static SPEventHubNode *SPEventFindHubNode(SPEventList *listconst char *event)
{
    SPEventHubNode *hubNode = NULL;
    SPEventList *node = NULL;

    SPEVENT_LIST_FOR_EACH(node, list) {
        hubNode = SPEVENT_LIST_ENTRY(node, SPEventHubNode, hubList);
        if(hubNode != NULL && strcmp(hubNode->event, event) == 0) {
            return hubNode;
        }
    }
    return NULL;
}

static SPEventEventNode *SPEventFindEventNode(SPEventList *eventList, SPEventHandle handle)
{
    SPEventEventNode *eventNode = NULL;
    SPEventList *node = NULL;

    SPEVENT_LIST_FOR_EACH(node, eventList) {
        eventNode = SPEVENT_LIST_ENTRY(node, SPEventEventNode, list);
        if(eventNode != NULL && handle == eventNode->handle) {
            return eventNode;
        }
    }
    return NULL;
}

SPEventEventNode *SPEventSubscribe(const char *event, SPEventHandle handle)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        hubNode = (SPEventHubNode *)SPEVENT_MALLOC(sizeof(SPEventHubNode));
        if(hubNode == NULL) {
            SPEVENT_PRINT("SPEVENT hub(%s) malloc failedrn", event);
            return NULL;
        }
        memset(hubNode->event, 0, SPEVENT_EVENT_NAME_LEN);
        memcpy(hubNode->event, event, strlen(event));
        SPEVENT_LIST_INSERT_AFTER(&g_hublist, &(hubNode->hubList));
        SPEVENT_LIST_INIT(&(hubNode->eventList));
    }

    eventNode = (SPEventEventNode *)SPEVENT_MALLOC(sizeof(SPEventEventNode));
    if(eventNode == NULL) {
        SPEVENT_PRINT("SPEVENT event(%s) malloc failedrn", event);
        return NULL;
    }
    eventNode->handle = handle;
    SPEVENT_LIST_INSERT_AFTER(&hubNode->eventList, &(eventNode->list));

    SPEVENT_PRINT("SPEVENT event(%s) Subscribe successrn", event);
    return eventNode;
}

bool SPEventUnsubscribe(const char *event, SPEventEventNode *node)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        SPEVENT_PRINT("SPEVENT hub(%s) find failedrn", event);
        return false;
    }
    eventNode = SPEventFindEventNode(&(hubNode->eventList), node->handle);
    if(eventNode == NULL) {
        SPEVENT_PRINT("SPEVENT event(%s) find failedrn", event);
        return false;
    }
    SPEVENT_LIST_REMOVE(&(eventNode->list));
    SPEVENT_FREE(eventNode);
    eventNode = NULL;

    SPEVENT_PRINT("SPEVENT event(%s) Unsubscribe successrn", event);
    return true;
}

bool SPEventPublish(const char *event, void *payload)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *node = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        SPEVENT_PRINT("SPEVENT hub(%s) find failedrn");
        return false;
    }
    SPEVENT_LIST_FOR_EACH(node, &(hubNode->eventList)) {
        eventNode = SPEVENT_LIST_ENTRY(node, SPEventEventNode, list);
        if(eventNode->handle) {
            eventNode->handle(event, payload);
        }
    }

    SPEVENT_PRINT("SPEVENT event(%s) Publish successrn", event);
    return true;
}

void SPEventClear(void)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *hubList = NULL;
    SPEventList *eventList = NULL;

    SPEVENT_LIST_FOR_EACH(hubList, &g_hublist) {
        hubNode = SPEVENT_LIST_ENTRY(hubList, SPEventHubNode, hubList);
        if(hubNode == NULL) {
            continue;
        }
        SPEVENT_LIST_FOR_EACH(eventList, &(hubNode->eventList)) {
            eventNode = SPEVENT_LIST_ENTRY(eventList, SPEventEventNode, list);
            if(eventNode == NULL) {
                continue;
            }
            SPEVENT_LIST_REMOVE(&(eventNode->list));
            SPEVENT_FREE(eventNode);
            eventNode = NULL;
        }

        SPEVENT_LIST_REMOVE(&(hubNode->hubList));
        SPEVENT_FREE(hubNode);
        hubNode = NULL;
    }
}

void RecvtInfoDump(void)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *hubList = NULL;
    SPEventList *eventList = NULL;
    int eventNodeCount = 0;

    SPEVENT_PRINT("SPEVENT list: rn");

    SPEVENT_LIST_FOR_EACH(hubList, &g_hublist) {
        hubNode = SPEVENT_LIST_ENTRY(hubList, SPEventHubNode, hubList);
        if(hubNode == NULL) {
            continue;
        }
        SPEVENT_PRINT("SPEVENT event(%s): ", hubNode->event);
        eventNodeCount = 0;
        SPEVENT_LIST_FOR_EACH(eventList, &(hubNode->eventList)) {
            eventNode = SPEVENT_LIST_ENTRY(eventList, SPEventEventNode, list);
            if(eventNode == NULL) {
                continue;
            }
            eventNodeCount++;
        }
        SPEVENT_PRINT("%drn", eventNodeCount);
    }
}

void SPEventInit(void)
{
    SPEVENT_LIST_INIT(&g_hublist);
}

void SPEventDeinit(void)
{
    SPEventClear();
}
  1. spevent.h文件说明:SPEvent的接口申明及SPEvent相关接口体的定义。
#ifndef __SPEVENT_H__
#define __SPEVENT_H__

#include "spevent_def.h"
typedef void (*SPEventHandle)(const char *event, void *payload);

typedef struct
{
    SPEventHandle handle;
    SPEventList list;
}SPEventEventNode;

typedef struct
{
    char event[SPEVENT_EVENT_NAME_LEN];
    SPEventList eventList;
    SPEventList hubList;
}SPEventHubNode;

void SPEventInit(void);

void SPEventDeinit(void);

SPEventEventNode *SPEventSubscribe(const char *event, SPEventHandle handle);

bool SPEventUnsubscribe(const char *event, SPEventEventNode *node);

bool SPEventPublish(const char *event, void *payload);

void SPEventClear(void);

void RecvtInfoDump(void);

#endif

超精简的SPEvent组件,实例:

#include 
void SPEventHandle1(const char *event, void *payload)
{
    SPEVENT_PRINT("Event1: %s, payload: %s", event, payload);
}

void SPEventHandle2(const char *event, void *payload)
{
    SPEVENT_PRINT(ent2: %s, payload: %s", event, payload);
}

int main()
{
    SPEventInit();
 
    SPEventEventNode *eventNode1 = SPEventSubscribe("Rice", SPEventHandle1);
    if(eventNode1 != NULL)
    {
        SPEventPublish("Rice""hello");
    } else {
        rlog_e("SPEVENT subscribe event failed");
    }

    SPEventEventNode *eventNode2 = SPEventSubscribe("Rice", SPEventHandle2);
    if(eventNode2 != NULL)
    {
        SPEventPublish("Rice""world");
    } else {
        rlog_e("SPEVENT subscribe event failed");
    }

    RecvtInfoDump();

    SPEventUnsubscribe("Rice", eventNode1);

    SPEventPublish("Rice""Hello world");

    RecvtInfoDump();
    return 0;
}

  • 结果:
SPEVENT event(Rice) Subscribe success
Event1: Rice, payload: hello
SPEVENT event(Rice) Publish success
SPEVENT event(Rice) Subscribe success
Event2: Rice, payload: world
Event1: Rice, payload: world
SPEVENT event(Rice) Publish success
SPEVENT list: 
SPEVENT event(Rice): 2
SPEVENT event(Rice) Unsubscribe success
Event2: Rice, payload: Hello world
SPEVENT event(Rice) Publish success
SPEVENT list: 
SPEVENT event(Rice): 1

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分