Qt 之 事件总线模型
文章目录
- Qt 之 开源事件总线模块
- libgitlevtbus
- 模型
- 模块
- Event
- EventBus
- Module
- ModuleDelegate
- TestCase
Qt 之 开源事件总线模块
libgitlevtbus
用到了libgitlevtbus
(libgitlevtbus)[https://github.com/lheric/libgitlevtbus]
- 介绍
libgitlevtbus 是一个基于Qt的开源的事件总线(消息总线 ) , BSD lisence. - 特征
1. Easy to use (c++11 feature supported: lambda expression, member function callback, ...) //C++11新特性,lambda表达式, 成员函数回调
2. Custom event support (carry custom parameters) //用户事件支持(可携带用户自定义的参数)
3. Events can be deliverd across threads //事件可以跨线程
- Demo
#include "gitlmodule.h"
#include <QDebug>int main(int argc, char *argv[])
{GitlModule cModule;/// subscribe to an eventcModule.subscribeToEvtByName("I am a test event",[](GitlEvent& rcEvt)->bool{qDebug() << "Hello GitlEvtBus!";return true;});GitlEvent cEvent("I am a test event"); ///< create an eventcEvent.dispatch(); ///< dispatch/// output: "Hello GitlEvtBus!"*/return 0;
}
模型
模块
该事件总线模型主要有以下几个类构成:
- Event
- EventBus
- Module
- ModuleDelegate
Event
该类是事件类,主要内容有:
- 事件名称
- 事件是否带有参数
- 事件参数的设置和获取
- 事件的发布(dispatch):调用Bus的Post接口,将事件派发到总线上,所有订阅该事件的module都会收到消息
- gitlevent.h
#ifndef GITLEVENT_H
#define GITLEVENT_H
#include <QString>
#include <QMap>
#include <QVariant>
#include "gitldef.h"
#include "gitleventparam.h"class GitlModule;
class GitlEventBus;/*!* \brief The GitlEvent class represents an event.* If you want to create an custom event by inherit GitlEvent, you ***MUST**** reimplement the 'clone' method in this class. This can be done by adding* VIRTUAL_COPY_PATTERN(subclassname) in the subclass. Otherwise the application* may crash*/
class GitlEvent
{/// virtual copy pattern, please add this macro to all the subclassCLONABLE(GitlEvent)public:GitlEvent( const QString& strEvtName );GitlEvent();virtual ~GitlEvent() {}/*!* \brief hasParameter if this event carries a specific parameter* \param strParam parameter name* \return*/bool hasParameter(QString strParam) const;/*!* \brief getParameter get the value of a specific parameter* \param strParam parameter name* \return parameter value, if it does not exist, return a default-constructed QVariant*/QVariant getParameter(const QString& strParam ) const;/*!* \brief setParameter set the value of a specific parameter* \param strParam parameter name* \param rvValue parameter value* \return*/bool setParameter(const QString& strParam, const QVariant& rvValue);/*!* \brief dispatch dispatch this event to event bus, all module subscribed to this event name will be notified.* \param pcEventBus If pcEventBus is NULL, it will find a global (default) event bus and post the event onto the bus.* Or you can specify another event bus.*/void dispatch(GitlEventBus *pcEventBus = NULL) const;protected: ADD_CLASS_FIELD(QString, strEvtName, getEvtName, setEvtName) ///< event nameADD_CLASS_FIELD_NOSETTER(GitlEventParam, cParameters, getParameters) ///< event parameters-value pair};
- gitlevent.cpp
#include "gitlevent.h"
#include "gitlmodule.h"
#include <QDebug>
#include "gitleventbus.h"
#include <QSharedPointer>
GitlEvent::GitlEvent( const QString& strEvtName )
{this->m_strEvtName = strEvtName;
}GitlEvent::GitlEvent()
{this->m_strEvtName = "UNKNOWN";
}bool GitlEvent::hasParameter(QString strParam) const
{return m_cParameters.hasParameter(strParam);
}QVariant GitlEvent::getParameter(const QString& strParam ) const
{return m_cParameters.getParameter(strParam);
}bool GitlEvent::setParameter(const QString& strParam, const QVariant& rvValue)
{m_cParameters.setParameter(strParam, rvValue);return true;
}void GitlEvent::dispatch(GitlEventBus* pcEventBus) const
{if(pcEventBus == NULL)GitlEventBus::getInstance()->post(*this);elsepcEventBus->post(*this);
}
EventBus
事件总线,在实际应用中,可以有多条事件总线,每条总线挂接不同的Module.
- 单例模式,在该类中,存在该类型的单例模式,当module未指定某个Bus时, 默认情况都使用该单例
- register: 将ModuleDelegate 的denotate关联至该总线的post接口发出的eventTriggered信号
- post: 将Event传递至总线。上述的Event中的dispatch接口,最终调用的某个Bus对象的post接口。
EventBus的核心: 注册ModuleDelegate, 发布Event 消息
- gitleventbus.h
#ifndef GITLEVENTBUS_H
#define GITLEVENTBUS_H#include <QList>
#include <QObject>
#include <QMutex>
#include <QMutexLocker>
#include <QSharedPointer>
#include "gitldef.h"
#include "gitlevent.h"
#include "gitlmodule.h"class GitlModuleDelegate;
/*!* \brief The GitlEventBus class represents the event bus*/
class GitlEventBus : public QObject
{Q_OBJECT
private:GitlEventBus();public:/*!* \brief create The safe way to explictly create a new event bus* \return*/static GitlEventBus *create();/*!* \brief registerModule connect a module to the event bus* \param pcModule* \return*/bool registerModule(GitlModuleDelegate *pcModule);/*!* \brief unregisterModule disconncet a module from the event bus* \param pcModule* \return*/bool unregisterModule(GitlModuleDelegate *pcModule);public slots:/*! send event to event bus*/void post(const GitlEvent &rcEvt) const;signals:/*! message to send*/void eventTriggered( QSharedPointer<GitlEvent> pcEvt ) const;///SINGLETON design patternSINGLETON_PATTERN_DECLARE(GitlEventBus)};#endif // GITLEVTBUS_H
- gitleventbus.cpp
#include "gitleventbus.h"
#include <QDebug>SINGLETON_PATTERN_IMPLIMENT(GitlEventBus)GitlEventBus::GitlEventBus()
{}GitlEventBus *GitlEventBus::create()
{return new GitlEventBus();
}/*! connect a module to the event bus*/
Q_DECLARE_METATYPE( QSharedPointer<GitlEvent> )
bool GitlEventBus::registerModule(GitlModuleDelegate* pcModule)
{ qRegisterMetaType< QSharedPointer<GitlEvent> >("QSharedPointer<GitlEvent>");connect(this, SIGNAL(eventTriggered(QSharedPointer<GitlEvent>) ),pcModule, SLOT (detonate (QSharedPointer<GitlEvent>) ),Qt::AutoConnection );return true;
}bool GitlEventBus::unregisterModule(GitlModuleDelegate *pcModule)
{return disconnect(this, NULL, pcModule, NULL);
}/*! send event to event bus*/
void GitlEventBus::post(const GitlEvent& rcEvt) const
{ QSharedPointer<GitlEvent> pcEvtCopy( rcEvt.clone() );/// notify modulesemit eventTriggered(pcEvtCopy);}
Module
Module 和 ModuleDelegate 使用了委托模式, 将实际的工作都交给了ModuleDelegate处理了。
委托模式:
一个对象接收到了请求,但是自己不处理,交给另外的对象处理,就是委托模式,例如 老板接到了活,
然后把活转手给了工人去做。
这里的Module有一个私有成员: ModuleDelegate, Event其实并不知道这一层关系的存在,在它眼里,只有BusEvent。
Module的行为(动作), 最后都转化成ModuleDelegate去执行了, 仿佛只是套了个壳。
- gitlmodule.h
#include <QSharedPointer>#include "gitldef.h"
#include "gitlevent.h"#include "gitlmoduledelegate.h"class GitlEventBus;/*!* \brief The GitlModule class represents a module*/class GitlModule
{public:/*** @brief GitlModule Represents a module in the event bus. It will keep listening to events* in the event bus and catch those it is interested in.* @param pcEventBus If pcEventBus it will find a gloabl event bus using singleton pattern.* Or you can specify an exsiting event bus.*/GitlModule(GitlEventBus* pcEventBus = NULL);/*!* \brief subscribeToEvtByName Subscribe to an event* \param strEvtName event name* \param pfListener listener callback function*/void subscribeToEvtByName(const QString& strEvtName,const GitlCallBack& pfListener );/*!* \brief unsubscribeToEvtByName Unsubscribe to an event* \param strEvtName event name*/void unsubscribeToEvtByName( const QString& strEvtName );/*!* \brief dispatchEvt Dispatch an event* \param rcEvt event*/void dispatchEvt(GitlEvent &rcEvt );/*!* \brief setModuleName Set the name of this module. That's ok if you do not* give a name to this module. But for better debugging, we recommend you name it.* \param strModuleName name for this module*/void setModuleName(QString strModuleName );/*** @brief getEventBus Get the event bus that this module is attached to* @return*/GitlEventBus* getEventBus();/*!* \brief detach Detach the module*/void detach();/*!* \brief attach Attach the module to a new event bus* \param pcEventBus*/void attach(GitlEventBus *pcEventBus);/// Delegate pattern/// Avoiding this class becoming a subclass of QObject/// (GUI class is based on QOBject, but QObject doesn't support virtual inheritance).ADD_CLASS_FIELD_PRIVATE( GitlModuleDelegate, cDelegate )
};#endif // GITLMODULE_H
- gitlmodule.cpp
#include "gitlmodule.h"
#include "gitleventbus.h"
#include <QDebug>GitlModule::GitlModule(GitlEventBus *pcEventBus) :m_cDelegate(this, pcEventBus)
{
}void GitlModule::subscribeToEvtByName( const QString& strEvtName,const GitlCallBack& pfListener )
{return m_cDelegate.subscribeToEvtByName(strEvtName, pfListener);
}void GitlModule::unsubscribeToEvtByName( const QString& strEvtName )
{return m_cDelegate.unsubscribeToEvtByName(strEvtName);
}void GitlModule::dispatchEvt( GitlEvent& rcEvt )
{m_cDelegate.dispatchEvt(rcEvt);
}void GitlModule::setModuleName( QString strModuleName )
{m_cDelegate.setModuleName(strModuleName);
}GitlEventBus *GitlModule::getEventBus()
{return m_cDelegate.getGitlEvtBus();
}void GitlModule::detach()
{m_cDelegate.detach();
}void GitlModule::attach(GitlEventBus *pcEventBus)
{m_cDelegate.attach(pcEventBus);
}
ModuleDelegate
由于真正干活的是这位老兄,所以,它除了和Module具有类似的接口,还需要额外维护一些私有成员,比如Module的名字,Event事件和回调接口的QMap关系表。
- gitlmoduledelegate.h
#ifndef GITLMODULEDELEGATE_H
#define GITLMODULEDELEGATE_H#include <QObject>
#include <QMap>
#include <QMutex>
#include <QMutexLocker>
#include <QSharedPointer>
#include <functional>
#include "gitldef.h"
#include "gitlevent.h"class GitlModule;
class GitlEventBus;///
/// \brief GitlCallBack gitl event callback function
///
typedef std::function<bool (GitlEvent&)> GitlCallBack;class GitlModuleDelegate : public QObject
{Q_OBJECTfriend class GitlModule; //can access the GitlModule
private:explicit GitlModuleDelegate(GitlModule *pcDelegator, GitlEventBus *pcEventBus = NULL);public:/*!* \brief subscribeToEvtByName listening to an event by name* \param strEvtName event name*/void subscribeToEvtByName( const QString& strEvtName,GitlCallBack pfListener );/*!* \brief subscribeToEvtByName not listening to an event by name* \param strEvtName event name*/void unsubscribeToEvtByName( const QString& strEvtName );/*!* \brief dispatchEvt dispatch an event to subscribers* \param pcEvt event*/void dispatchEvt(const GitlEvent &rcEvt ) const;/*!* \brief detach Detach the module*/void detach();/*!* \brief attach Attach the module to a new event bus* \param pcEventBus*/void attach(GitlEventBus *pcEventBus);public slots:/*!* \brief detonate notifyed by event bus* \param cEvt* \return*/bool detonate( QSharedPointer<GitlEvent> pcEvt );protected:bool xIsListenToEvt(const QString& strEvtName);ADD_CLASS_FIELD( QString, strModuleName, getModuleName, setModuleName )ADD_CLASS_FIELD_PRIVATE( CONCATE(QMap<QString, GitlCallBack>), cListeningEvts )ADD_CLASS_FIELD_NOSETTER( GitlEventBus*, pcGitlEvtBus, getGitlEvtBus )ADD_CLASS_FIELD_PRIVATE(GitlModule*, pcDelegator)};#endif // GITLMODULEDELEGATE_H
- gitlmoduledelegate.cpp
#include "gitlmoduledelegate.h"
#include "gitleventbus.h"
#include <QDebug>
#include <iostream>
using namespace std;
GitlModuleDelegate::GitlModuleDelegate(GitlModule *pcDelegator, GitlEventBus* pcEventBus)
{m_pcDelegator = pcDelegator;if(pcEventBus == NULL)m_pcGitlEvtBus = GitlEventBus::getInstance();elsem_pcGitlEvtBus = pcEventBus;m_pcGitlEvtBus->registerModule(this); //调用eventBus注册moduleDelegatem_strModuleName = "undefined_module_name";}void GitlModuleDelegate::subscribeToEvtByName(const QString& strEvtName, GitlCallBack pfListener )
{m_cListeningEvts.insert(strEvtName, pfListener);return;
}void GitlModuleDelegate::unsubscribeToEvtByName( const QString& strEvtName )
{m_cListeningEvts.remove(strEvtName);
}bool GitlModuleDelegate::detonate(QSharedPointer<GitlEvent> pcEvt )
{QMap<QString, std::function<bool (GitlEvent&)>>::iterator p =m_cListeningEvts.find(pcEvt->getEvtName());if( p != m_cListeningEvts.end() ){(p.value())(*pcEvt.data());}return true;
}bool GitlModuleDelegate::xIsListenToEvt( const QString& strEvtName )
{return m_cListeningEvts.contains(strEvtName);
}void GitlModuleDelegate::dispatchEvt( const GitlEvent& rcEvt ) const
{if(m_pcGitlEvtBus != NULL)m_pcGitlEvtBus->post(rcEvt);
}void GitlModuleDelegate::detach()
{if(m_pcGitlEvtBus != NULL)m_pcGitlEvtBus->unregisterModule(this);m_pcGitlEvtBus = NULL;
}void GitlModuleDelegate::attach(GitlEventBus *pcEventBus)
{if(pcEventBus == NULL)return;detach();m_pcGitlEvtBus = pcEventBus;m_pcGitlEvtBus->registerModule(this);
}
TestCase
#include <QCoreApplication>
#include <iostream>
#include <QtTest/QtTest>
#include <QTest>
#include <QSharedPointer>
#include <QString>
#include <functional>
#include "gitldef.h"
#include "gitlmodule.h"
#include "gitleventbus.h"
using namespace std;/// test event bus
class TestModule : public GitlModule //继承自GitlModule
{public:TestModule(GitlEventBus* pcEventBus = NULL):GitlModule(pcEventBus){this->m_bNotified = false;}void subscribeInsideClass(){subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK(TestModule::callback)); //绑定内部的callback函数}bool callback( GitlEvent& rcEvt){Q_UNUSED(rcEvt)this->m_bNotified = true;return true;}ADD_CLASS_FIELD(bool, bNotified, getNotified, setNotified)
};/// custom event, 用户自定义事件,集成GitlEvent
class CustomEvent : public GitlEvent
{CLONABLE(CustomEvent)
public:CustomEvent( const QString& strEvtName ) : GitlEvent(strEvtName) { m_strCustomVar = "Custom String"; //自定义私有成员}ADD_CLASS_FIELD(QString, strCustomVar, getCustomVar, setCustomVar)
};/// 用户自定义事件监听模块,继承自GitlModule
class CustomEventListener : public GitlModule
{public:CustomEventListener(){this->m_bNotified = false;}bool callback( GitlEvent& rcEvt){CustomEvent& pcCusEvt = static_cast<CustomEvent&>(rcEvt);this->m_bNotified = true;this->m_strCustomVar = pcCusEvt.getCustomVar();return true;}ADD_CLASS_FIELD(bool, bNotified, getNotified, setNotified)ADD_CLASS_FIELD(QString, strCustomVar, getCustomVar, setCustomVar)
};/// test case
class TestCase : public QObject
{Q_OBJECTprivate slots:void lamdaListening(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1",[&](GitlEvent& e)->bool{Q_UNUSED(e)cModule.setNotified(true);return true;});QVERIFY(!cModule.getNotified()); //QVERIFY(condition)GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(cModule.getNotified());}void listenInsideClass(){TestModule cModule;cModule.subscribeInsideClass();QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cEvt.dispatch();QVERIFY(cModule.getNotified());}void listenOutsideClass(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule, TestModule::callback));QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(cModule.getNotified());}void unsubscribe(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule, TestModule::callback));cModule.unsubscribeToEvtByName("TEST_EVENT_1");QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(!cModule.getNotified());}/// 一对多,一个事件,多个Module关注void oneToMany(){TestModule cSender;TestModule cModule1;TestModule cModule2;TestModule cModule3;cModule1.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule1, TestModule::callback));cModule2.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule2, TestModule::callback));cModule3.subscribeToEvtByName("TEST_EVENT_2", MAKE_CALLBACK_OBJ(cModule3, TestModule::callback));GitlEvent cEvt1("TEST_EVENT_1");cSender.dispatchEvt(cEvt1); //某个Module 派发Event, 而其他Module 关注该Event的后进行处理QVERIFY(cModule1.getNotified());QVERIFY(cModule2.getNotified());QVERIFY(!cModule3.getNotified());GitlEvent cEvt2("TEST_EVENT_2");cSender.dispatchEvt(cEvt2);QVERIFY(cModule3.getNotified());}/// 用户自定义事件测试void customEventTest(){CustomEventListener cModule;cModule.subscribeToEvtByName("TEST_EVENT_1",MAKE_CALLBACK_OBJ(cModule, CustomEventListener::callback));CustomEvent cEvt("TEST_EVENT_1");cEvt.dispatch();qDebug()<<__FUNCTION__<<cModule.getNotified();qDebug()<<__FUNCTION__<<cModule.getCustomVar();QVERIFY(cModule.getNotified());QVERIFY(cModule.getCustomVar() == QString("Custom String")); //用户自定义的参数, 需要关注CModule的getCustomVar}///多个EventBus和多个Module对象void multiplyEventBus(){GitlEventBus* pcBus1 = GitlEventBus::create(); TestModule cModule1(pcBus1); TestModule cModule2(pcBus1);GitlEventBus* pcBus2 = GitlEventBus::create(); TestModule cModule3(pcBus2); TestModule cModule4(pcBus2);/// all module are listening to the same events, but on different event buses.cModule1.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule1, TestModule::callback));cModule2.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule2, TestModule::callback));cModule3.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule3, TestModule::callback));cModule4.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule4, TestModule::callback));/// eventCustomEvent cEvt("TEST_EVENT_1");/// no one get notified because no module is attached to the default event buscEvt.dispatch();QVERIFY(!cModule1.getNotified());QVERIFY(!cModule2.getNotified());QVERIFY(!cModule3.getNotified());QVERIFY(!cModule4.getNotified());/// this will only notify module 1 & 2cEvt.dispatch(pcBus1);QVERIFY(cModule1.getNotified());QVERIFY(cModule2.getNotified());QVERIFY(!cModule3.getNotified());QVERIFY(!cModule4.getNotified());/// this will notify module 3 & 4cEvt.dispatch(cModule3.getEventBus());QVERIFY(cModule3.getNotified());QVERIFY(cModule4.getNotified());/// make sure everyone is attached to the correct event busQVERIFY(cModule1.getEventBus() == pcBus1);QVERIFY(cModule2.getEventBus() == pcBus1);QVERIFY(cModule3.getEventBus() == pcBus2);QVERIFY(cModule4.getEventBus() == pcBus2);/// create cModule5TestModule cModule5(pcBus1);cModule5.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule5, TestModule::callback));cEvt.dispatch(pcBus2);QVERIFY(!cModule5.getNotified());cModule5.attach(pcBus2);cEvt.dispatch(pcBus2);QVERIFY(cModule5.getNotified());}
};/// test main
QTEST_MAIN(TestCase)
#include "testcase.moc"
- 运行测试结果
********* Start testing of TestCase *********
Config: Using QtTest library 5.14.2, Qt 5.14.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 5.3.1 20160406 (Red Hat 5.3.1-6))
PASS : TestCase::initTestCase()
PASS : TestCase::lamdaListening()
PASS : TestCase::listenInsideClass()
PASS : TestCase::listenOutsideClass()
PASS : TestCase::unsubscribe()
PASS : TestCase::oneToMany()
QDEBUG : TestCase::customEventTest() customEventTest true
QDEBUG : TestCase::customEventTest() customEventTest "Custom String"
PASS : TestCase::customEventTest()
PASS : TestCase::multiplyEventBus()
PASS : TestCase::cleanupTestCase()
Totals: 9 passed, 0 failed, 0 skipped, 0 blacklisted, 1ms
********* Finished testing of TestCase *********
Qt 之 事件总线模型相关推荐
- EventBridge 事件总线及 EDA 架构解析
简介:EventBridge 是事件驱动的具体落地产品,也是 EDA 的最佳实践方式. 作者:肯梦 作为 Gartner 定义的 10 大战略技术趋势之一,事件驱动架构(EDA)逐渐成为主流技术架构. ...
- Android事件总线还能怎么玩?
作者简介:何红辉,Android工程师,现任职于友盟. 顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity.Fragment.Service等组件 ...
- 8.QT的事件循环与事件发送相关类
一.QT的事件发送类QCoreApplication QT使用QCoreApplication类为Qt程序提供了事件循环机制.该类继承QObject.QCoreApplication包含主事件循环,来 ...
- Android--Otto事件总线 -- 组件之间通讯框架使用 --模式解析
前言:Otto事件总线 -- 组件之间通讯框架 对于之前的情况activity之间或者fragment之间等跳转传值一般都是用bundle.intent等,从activityA --- activit ...
- Qt中的自定义模型类
文章目录 1 Qt中的通用模型类 1.1 Qt中的通用模型类 1.2 Qt中的变体类型QVariant 2 自定义模型类 2.1 自定义模型类设计分析 2.2 自定义模型类数据层.数据表示层.数据组织 ...
- 阿里云 Serverless 事件总线 EventBridge 重磅发布
简介:近年来,随着云原生和 Serverless 概念的深入人心,事件驱动再一次成为了云应用架构领域的热门词汇.在 2018 年,Gartner 评估报告将 Event-Driven Model 列为 ...
- QT的事件分发、事件过滤器详解
一.事件的流向 QT的各种控件(QObject的子类)都有事件处理成员函数,例如: bool QObject::event(QEvent *e);//所有事件 dragEnterEvent(QDrag ...
- 如何通过本地化事件正确实现微服务内部强一致性,事件总线跨微服务间最终一致性...
目录 设计重点 流程图 伪代码 2.1. PublishEvent 2.2. SubscribeEvent 2.3. Publisher 2.4. Subscriber 微服务 强一致性 3.1 Pu ...
- Android之事件总线EventBus详解
顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity.Fragment.Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码 ...
最新文章
- GeoIP的使用 - PHP版
- 通关制单机器人_2020关务节|“数字供应链与智能通关”论坛——如何打造云上跨境贸易生态圈...
- NLP任务非Transformer不可?
- 【资源】100+本数据科学电子书
- oracle发送邮件
- 亲和属性和链路管理组的TE隧道路径控制原理
- zoj 3791 An Easy Game
- 如何理解Bounce Rate和Exit Rate
- java设置面板的大小_java – 设置面板的大小
- 金立手机用60亿“砸死”了自己
- cs python课程 加州大学_最新盘点!全球顶尖大学CS+数据科学的免费在线课程,共81个...
- 跑步进入全站 HTTPS ,这些经验值得你看看
- 如何应用Matlab plot画点
- 六个步骤 教你搭建Ubuntu nfs服务器
- php发起预约申请,php版微信公众平台实现预约提交后发送email的方法
- 大数据分析的学习感悟
- 互联网经济催生了一些新职业,带来新机遇!
- 华硕服务器安装系统安装教程,华硕u盘安装系统教程
- php主机卫士,Bypass 360主机卫士SQL注入防御(附tamper脚本)
- 项目二:python爬取豆瓣电影信息并分析
热门文章
- hive_hbase一个综合练习题目总共包括以下部分
- 华为Mate系列主要参数
- 陈学贤华南理工大学计算机专业,张见威 - 华南理工大学 - 计算机科学与工程学院...
- 对Redis数据库的学习!
- 做外贸十大不能接的订单!
- 使用opencv实现通过摄像头自动输入阿里云身份宝验证码
- 抖音直播运营分析:深入解读直播带货运营那些专业术语
- 绝对良心提供百度网盘的jdk1.8源码下载包含sun包的
- react报错Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
- -Cannot use v-for on stateful component root element because it renders multiple elements