PDA

View Full Version : QNetworkAccessManager multiple connect to finished signal



Swiftie
6th February 2017, 11:49
Hello! I tried to make multiple connections to QNetworkAccessManager finished() signal.
I have ApiHandler class which makes a request handling.
And need to call a specific function after request finished (make use of slots).

I tried to make realization as below. But always worked only one connect (which is first one) - validateTokenFinished.
Explain how to properly realize what i want.
Thanks!

userapi.h


// includes...
class UserApi : public QObject
{
Q_OBJECT
public:
explicit UserApi(QObject *parent = 0);
void updateFriendList();

signals:

public slots:
void friendListUpdated(QNetworkReply* reply);

};

userapi.cpp


// includes
UserApi::UserApi(QObject *parent) :
QObject(parent)
{
}

void UserApi::updateFriendList()
{
qDebug("UserApi::updateFriendList()");

QMap<QString, QString> m;
// ...

ApiHandler *a = new ApiHandler;

connect(a->getManager(), SIGNAL(finished(QNetworkReply*)), this, SLOT(friendListUpdated(QNetworkReply*)));

a->makeRequest("friends.get", m);
}

//! Not called at all
void UserApi::friendListUpdated(QNetworkReply* reply)
{
QJsonDocument j = QJsonDocument::fromJson(reply->readAll());
QJsonObject getjson = j.object();


qDebug() << "UserApi::friendListUpdated()" << getjson;

reply->deleteLater();
}


toolsapi.h


// includes...
class ToolsApi : public QObject
{
Q_OBJECT
public:
explicit ToolsApi(QObject *parent = 0);
void validateToken();

signals:

public slots:
void validateTokenFinished(QNetworkReply* reply);

};

toolsapi.cpp


// includes
ToolsApi::ToolsApi(QObject *parent) :
QObject(parent)
{
}

void ToolsApi::validateToken()
{
qDebug("ToolsApi::validateToken()");

QMap<QString, QString> m;
// ...

ApiHandler *a = new ApiHandler;

connect(a->getManager(), SIGNAL(finished(QNetworkReply*)), this, SLOT(validateTokenFinished(QNetworkReply*)));

a->makeRequest("tools.isTokenValid", m);
}

void ToolsApi::validateTokenFinished(QNetworkReply* reply)
{
QJsonDocument j = QJsonDocument::fromJson(reply->readAll());
QJsonObject getjson = j.object();

qDebug() << "ToolsApi::validateTokenFinished()" << getjson;

reply->deleteLater();
}


mainwindow.h


// includes...
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
ToolsApi c;

public slots:
void on_testButton_clicked();

private:
Ui::MainWindow *ui;
};


mainwindow.cpp


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// ...
ui->setupUi(this);

c.validateToken();
}

void MainWindow::on_testButton_clicked()
{
UserApi friends;

friends.updateFriendList();
}



And an ApiHandler class

apihandler.h


// includes...
class ApiHandler : public QObject
{
Q_OBJECT
public:
explicit ApiHandler(QObject *parent = 0);
void makeRequest(QString method, QMap<QString, QString> parameters);
QNetworkAccessManager* getManager();

private:
QUrl buildCall(QString method, QMap<QString, QString> parameters);
QNetworkAccessManager* manager;

signals:

public slots:
void slotReadyRead();
void slotError(QNetworkReply::NetworkError error);
void slotSslErrors(QList<QSslError> errorList);
};

apihandler.cpp


// includes...

ApiHandler::ApiHandler(QObject *parent) :
QObject(parent)
{
this->manager = new QNetworkAccessManager(this);
}

QUrl ApiHandler::buildCall(QString method, QMap<QString, QString> parameters)
{
// ...
}

void ApiHandler::makeRequest(QString method, QMap<QString, QString> parameters)
{
QUrl url = this->buildCall(method, parameters);

QNetworkRequest request;

request.setUrl(url);
request.setRawHeader("User-Agent", "Test Client");

QNetworkReply *reply = this->manager->get(request);

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
}

void ApiHandler::slotError(QNetworkReply::NetworkError error)
{
//...
}

void ApiHandler::slotSslErrors(QList<QSslError> errorList)
{
//...
}

QNetworkAccessManager* ApiHandler::getManager()
{
return this->manager;
}

jefftee
6th February 2017, 17:49
Hard to tell without studying your code a little more, but perhaps the reply has been deleted by your toolsapi.cpp line 28 before the 2nd connected slot can be executed? While using deleteLater() is a common approach, I don't believe it's documented exactly how much later the instance will be deleted and will remove all connected slots as part of the deconstruction.

Try commenting out that deleteLater() and see if your 2nd connected slot gets executed.

Swiftie
6th February 2017, 18:29
Hello! Thats almost all code (network communication part).

I've already tried without deleteLater() but nothing changed.

anda_skoa
7th February 2017, 08:06
You are creating "UserApi friends" on the stack, it will be destroyed immediately after you call updateFriendsList().

Btw, a QNetworkAccessManager can handle multiple requests, you don't necessarily have to create a new one for each request.

Cheers,
_

Swiftie
7th February 2017, 08:33
Howdy! So i should use singleton of ApiHandler or there is a different approach (with example)?
Thanks in advance!

jefftee
7th February 2017, 18:08
You are creating "UserApi friends" on the stack, it will be destroyed immediately after you call updateFriendsList().
Nice catch!

anda_skoa
8th February 2017, 10:03
Howdy! So i should use singleton of ApiHandler or there is a different approach (with example)?


Just saying you don't have to use a different instance each time.

Cheers,
_