上一篇分析到原理上,主要讲述的是builder模式设计与HttpService,HttpRequest的原理和实现。本篇将会讲到HttpResponse的响应机制与自动推导接收者(槽函数)的实现。
先从问题入手,看下面onResponse代码,为什么会存在第三个参数HttpResponse::SupportMethod type呢?响应只需接收者与槽函数就可以了吧。这里写槽函数是因为用得最多,实际上也可以写成信号,相当于再次转发。
HttpRequest &onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type = HttpResponse::AutoInfer);
onResponse函数
1. SupportMethod枚举
(1) 写的是支持const char *slot的类型;
(2) 当使用AutoInfer则系统会自动通过槽函数推导出对应的枚举值。
/*
* Support Reflex Method
* default: AutoInfer
* AutoInfer: Automatic derivation based on type
*/
enum SupportMethod {
AutoInfer,
onResponse_QNetworkReply_A_Pointer, /* method: void function(QNetworkReply* reply); Is_AutoInfer: true */
onResponse_QByteArray, /* method: void function(QByteArray data); Is_AutoInfer: true */
onResponse_QVariantMap, /* method: void function(QVariantMap map); Is_AutoInfer: true */
onDownloadProgress_qint64_qint64, /* method: void function(qint64 bytesReceived, qint64 bytesTotal); Is_AutoInfer: true */
onError_QNetworkReply_To_NetworkError, /* method: void function(QNetworkReply::NetworkError error); Is_AutoInfer: true */
onError_QString, /* method: void function(QString errorString); Is_AutoInfer: true */
onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer, /* method: void function(QNetworkReply::NetworkError error, QNetworkReply* reply); Is_AutoInfer: true */
onError_QString_QNetworkReply_A_Poniter/* method: void function(QString errorString, QNetworkReply* reply); Is_AutoInfer: true */
};
SupportMethod枚举
2. SupportMethod的使用
(1) 截取一段上面SupportMethod枚举的注释可以看到"method: void function(QNetworkReply* reply); Is_AutoInfer: true";
(2)onResponse_QNetworkReply_A_Pointer枚举值的槽函数可以支持注释上的格式,就如行1写法。
onResponse(this, SLOT(finish(QNetworkReply*)));
onResponse(this, SLOT(finish(QByteArray)));
onResponse(this, SLOT(finish(QVariantMap)));
onResponse(this, SLOT(finish(QNetworkReply::NetworkError)));
onResponse(this, SLOT(QString));
onResponse(this, SLOT(QNetworkReply::NetworkError, QNetworkReply*));
onResponse(this, SLOT(QString, QNetworkReply*));
onResponse实际用法
3. 自动推导类型的应用
(1) 调用onResponse则会将type,receiver与slot存放在m_slotsMap私有变量里面,方便提供给HttpResponse类绑定QNetworkReply的信号使用;
(2) 由于onError的slot都支持自动推导类型,则它也是调用onResponse。
HttpRequest &HttpRequest::onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type)
{
m_slotsMap.insert(NUMBER_TO_STRING(type), {{slot, receiver)}});
return *this;
}
HttpRequest &HttpRequest::onError(const QObject *receiver, const char *slot)
{
return onResponse(receiver, slot, HttpResponse::AutoInfer);
}
onResponse与onError实现
4. HttpResponse的构造函数实现
(1) 当HttpRequest类装载完毕调用exec则会实例化HttpResponse类,传递的参数为QNetworkReply与HttpRequest的m_slotsMap;
(2) HttpResponse主要的作用为二次处理QNetworkReply(信号的转发)。
HttpResponse::HttpResponse(QNetworkReply *parent, const QMultiMap<QString, QMap<QString, const QObject *> > &slotsMap)
: QNetworkReply(parent), m_slotsMap(slotsMap)
{
slotsMapOperation(m_slotsMap);
connect(parent, SIGNAL(finished()), this, SLOT(onFinished()));
connect(parent, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
connect(parent, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
if (!QNetworkConfigurationManager().isOnline())
onError(QNetworkReply::UnknownNetworkError);
}
HttpResponse构造函数
5. slotsMapOperation的实现
HttpResponse构造时候则会调用slotsMapOperation函数,该函数主要作用是绑定HttpRequest的onResponse的槽函数,使得网络响应到来时候能够响应槽函数达到异步的效果。
(1) methodParams[key].value("signal")的methodParams是一个预先设定的容器;
(2) 这里的key实则是onResponse里面SupportMethod枚举的值,只是被转换为字符串;
(3) 用key来查找(methodParams)到对应的数据,这里查找的是信号属性;
(4) 查找到该信号属性则绑定它。
void HttpResponse::slotsMapOperation(QMultiMap<QString, QMap<QString, const QObject *> > &slotsMap)
{
autoInfterConvertedSupportMethod(slotsMap);
QMapIterator<QString, QMap<QString, const QObject *> > iter(slotsMap);
while (iter.hasNext()) {
iter.next();
const QString &key = iter.key();
const QMap<QString, const QObject *> &slotMap = iter.value();
const QObject *receiver = slotMap.first();
const QString &receiverSlot = slotMap.firstKey();
if (methodParams.contains(key)) {
connect(this,
methodParams[key].value("signal").toString().toStdString().data(),
receiver,
receiverSlot.toStdString().data(),
Qt::QueuedConnection);
}
}
}
slotsMapOperation函数
6. methodParams的预定义
methodParams看起来有点奇怪,但一层一层分解就可以看清楚,主要该值复杂是在容器里面嵌套了一个容器。
(1) 第一个容器的key为保存SupportMethod枚举值的字符串,N2S宏为将数字转为字符串;
(2) 第二个容器保存的是types,signal,isAutoInfer字段信息。
static const QMap<QString, QMap<QString, QVariant>> methodParams =
{
{
N2S(HttpResponse::onResponse_QNetworkReply_A_Pointer),
{
{"types", QStringList({T2S(QNetworkReply*)})},
{"signal", SIGNAL(finished(QNetworkReply*))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onResponse_QByteArray),
{
{"types", QStringList({T2S(QByteArray)})},
{"signal", SIGNAL(finished(QByteArray))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onResponse_QVariantMap),
{
{"types", QStringList({T2S(QVariantMap)})},
{"signal", SIGNAL(finished(QVariantMap))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onDownloadProgress_qint64_qint64),
{
{"types", QStringList({T2S(qint64), T2S(qint64)})},
{"signal", SIGNAL(downloadProgress(qint64, qint64))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onError_QNetworkReply_To_NetworkError),
{
{"types", QStringList({T2S(QNetworkReply::NetworkError)})},
{"signal", SIGNAL(error(QNetworkReply::NetworkError))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onError_QString),
{
{"types", QStringList({T2S(QString)})},
{"signal", SIGNAL(error(QString))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer),
{
{"types", QStringList({T2S(QNetworkReply::NetworkError), T2S(QNetworkReply*)})},
{"signal", SIGNAL(error(QNetworkReply::NetworkError, QNetworkReply*))},
{"isAutoInfer", true}
}
},
{
N2S(HttpResponse::onError_QString_QNetworkReply_A_Poniter),
{
{"types", QStringList({T2S(QString), T2S(QNetworkReply*)})},
{"signal", SIGNAL(error(QString, QNetworkReply*))},
{"isAutoInfer", true}
}
},
};
methodParams类型
7. 从"types"到推导槽函数类型
(1) getSupportMethod传入的容器参数为槽函数(QString)与接收者对象(const QObject*);
(2) 槽函数的传入,由于传入的是字符串则可以自动解析到槽的参数类型;
(3) extractSlot就是转换槽函数的数据类型为QStringList;
(4) 然后methodParams逐一判断是否有存在相同的"types"的值,如果存在,则自动找到methodParams的key值。
/* from slotMap get [SupportMethod] */
static QString getSupportMethod(const QMap<QString, const QObject *> &slotMap) {
QMapIterator<QString, QMap<QString, QVariant>> iter(methodParams);
QString receiverSlot = slotMap.firstKey();
QString slot;
QStringList slotTypes;
extractSlot(receiverSlot, slot, slotTypes);
while (iter.hasNext()) {
iter.next();
QString key = iter.key();
QMap<QString, QVariant> value = iter.value();
if (slotTypes == value.value("types").toStringList()) {
return key;
}
}
return "";
}
getSupportMethod函数
8. 信号的转发机制
(1) 在"5. slotsMapOperation的实现"连接的信号与槽函数,哪里的连接信号则是下买你所描述的信号;
signals:
void finished(QNetworkReply *reply);
void finished(QByteArray data);
void finished(QVariantMap map);
void error(QString errorString);
void error(QNetworkReply::NetworkError error);
void error(QString errorString, QNetworkReply *reply);
void error(QNetworkReply::NetworkError error, QNetworkReply *reply);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
HttpResponse信号
(2) 下列为onFinished函数它在构造函数时候就已经绑定了QNetworkReply的finished信号,而其他信号也被同样的方式绑定。当对应的finished信号被发射的时候,就会触发先前已经绑定与传入的槽函数。就实现了信号的转发功能。
void HttpResponse::onFinished()
{
QNetworkReply *reply = (QNetworkReply *)this->parent();
if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QNetworkReply_A_Pointer))) {
emit finished(reply);
}
else if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QByteArray))) {
QByteArray result = reply->readAll();
emit finished(result);
reply->deleteLater();
}
else if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QVariantMap))) {
QByteArray result = reply->readAll();
emit finished(QJsonDocument::fromJson(result).object().toVariantMap());
reply->deleteLater();
}
}
onFinished槽函数
最后在实现HttpResponse时实现比较繁琐,但是为了方便以后扩展需要。后续还会支持lambda表达式。