使用 Q_GLOBAL_STATIC 实现(线程安全)
全局消息中心
// GlobalMessageCenter.h
#pragma once
#include <QObject>
#include <QHash>
#include <QVector>
#include <QVariant>
#include <functional>
#include <QMutex>
class GlobalMessageCenter : public QObject
{
Q_OBJECT
public:
// 单例访问
static GlobalMessageCenter* instance();
// 订阅消息
void subscribe(const QString& messageType, QObject* receiver,
const char* slot, Qt::ConnectionType type = Qt::AutoConnection);
// 取消订阅
void unsubscribe(const QString& messageType, QObject* receiver);
void unsubscribeAll(QObject* receiver);
// 发布消息
void publish(const QString& messageType, const QVariant& data = QVariant());
// 同步发布(阻塞直到所有处理器完成)
void publishSync(const QString& messageType, const QVariant& data = QVariant());
signals:
// 全局信号
void messagePublished(const QString& messageType, const QVariant& data);
private:
explicit GlobalMessageCenter(QObject* parent = nullptr);
~GlobalMessageCenter();
struct Subscription {
QObject* receiver;
const char* slot;
Qt::ConnectionType connectionType;
};
QHash<QString, QVector<Subscription>> m_subscriptions;
QMutex m_mutex;
};
// GlobalMessageCenter.cpp
#include "GlobalMessageCenter.h"
Q_GLOBAL_STATIC(GlobalMessageCenter, globalMessageCenter)
GlobalMessageCenter::GlobalMessageCenter(QObject* parent)
: QObject(parent)
{
}
GlobalMessageCenter::~GlobalMessageCenter()
{
QMutexLocker locker(&m_mutex);
m_subscriptions.clear();
}
GlobalMessageCenter* GlobalMessageCenter::instance()
{
return globalMessageCenter;
}
void GlobalMessageCenter::subscribe(const QString& messageType, QObject* receiver,
const char* slot, Qt::ConnectionType type)
{
QMutexLocker locker(&m_mutex);
Subscription sub{receiver, slot, type};
m_subscriptions[messageType].append(sub);
// 连接全局信号
connect(this, &GlobalMessageCenter::messagePublished,
receiver, slot, type);
qDebug() << "订阅消息:" << messageType
<< "接收者:" << receiver
<< "槽函数:" << slot;
}
void GlobalMessageCenter::unsubscribe(const QString& messageType, QObject* receiver)
{
QMutexLocker locker(&m_mutex);
if (m_subscriptions.contains(messageType)) {
auto& subs = m_subscriptions[messageType];
for (auto it = subs.begin(); it != subs.end(); ) {
if (it->receiver == receiver) {
disconnect(this, &GlobalMessageCenter::messagePublished,
receiver, it->slot);
it = subs.erase(it);
} else {
++it;
}
}
if (subs.isEmpty()) {
m_subscriptions.remove(messageType);
}
}
}
void GlobalMessageCenter::unsubscribeAll(QObject* receiver)
{
QMutexLocker locker(&m_mutex);
for (auto it = m_subscriptions.begin(); it != m_subscriptions.end(); ) {
auto& subs = it.value();
for (auto subIt = subs.begin(); subIt != subs.end(); ) {
if (subIt->receiver == receiver) {
disconnect(this, &GlobalMessageCenter::messagePublished,
receiver, subIt->slot);
subIt = subs.erase(subIt);
} else {
++subIt;
}
}
if (subs.isEmpty()) {
it = m_subscriptions.erase(it);
} else {
++it;
}
}
}
void GlobalMessageCenter::publish(const QString& messageType, const QVariant& data)
{
qDebug() << "发布异步消息:" << messageType << "数据:" << data;
emit messagePublished(messageType, data);
}
void GlobalMessageCenter::publishSync(const QString& messageType, const QVariant& data)
{
QMutexLocker locker(&m_mutex);
qDebug() << "发布同步消息:" << messageType << "数据:" << data;
if (m_subscriptions.contains(messageType)) {
const auto& subs = m_subscriptions[messageType];
for (const auto& sub : subs) {
if (sub.receiver) {
// 直接调用槽函数
QMetaObject::invokeMethod(sub.receiver, sub.slot + 1, // 去掉 SIGNAL() 的括号
Qt::DirectConnection,
Q_ARG(QString, messageType),
Q_ARG(QVariant, data));
}
}
}
}
/*
slot + 1 可能会有风险 替换为以下方法
// 推荐
void callSlotSafely(QObject* obj, const char* slot) {
if (!slot || slot[0] == '\0') {
return;
}
// 验证格式
if (slot[0] != '1' && slot[0] != '2') {
qWarning() << "Invalid signature:" << slot;
return;
}
// 使用方法名
const char* methodName = slot + 1;
// 或者使用 QMetaMethod
QString signature = QString::fromUtf8(slot + 1);
const QMetaObject* meta = obj->metaObject();
int methodIndex = meta->indexOfMethod(signature.toUtf8().constData());
if (methodIndex >= 0) {
QMetaMethod method = meta->method(methodIndex);
method.invoke(obj, Qt::QueuedConnection);
}
}
*/
使用 示例
// ReceiverWidget.cpp
#include "ReceiverWidget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
ReceiverWidget::ReceiverWidget(QWidget* parent)
: QWidget(parent)
{
setupUI();
registerMessages();
}
ReceiverWidget::~ReceiverWidget()
{
GlobalMessageCenter::instance()->unsubscribeAll(this);
}
void ReceiverWidget::registerMessages()
{
auto globalMsgCenter = GlobalMessageCenter::instance();
// 使用 GlobalMessageCenter
globalMsgCenter->subscribe(MessageType::USER_LOGIN, this,
SLOT(onGlobalMessage(QString, QVariant)));
}
void ReceiverWidget::onUserLogin(const QVariant& data)
{
}
void ReceiverWidget::onGlobalMessage(const QString& messageType, const QVariant& data)
{
QString log = QString("[%1] 全局消息中心收到: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(messageType);
m_logText->append(log);
}