Network Requests in Native BlackBerry 10

I was recently working on a native BlackBerry 10 project and ran into an interesting problem where the slot (handler) dealing with a network response was being called multiple times for a single request. Specifically, I discovered that it was being called once on the first network request, twice on the second, three times on the third, etc.

This was happening because network requests were following this anti-pattern:

QNetworkRequest requestOne = makeTypeOneRequest();
QNetworkAccessManager networkManager = getNetworkManagerInstance();
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onTypeOneRequestFinished(QNetworkReply*)));
networkManager.get(requestOne);

In this example, a connection is formed to direct the network manager’s finished() signal, called with the network response, to the slot onTypeOneRequestFinished(). This works fine once, but the next time a network request is dispatched and a new slot is connected, the first connection will still be present, and the finished signal from the network manager will be sent to both slots, and all three slots after the third request, etc.

There is no mechanism that I know of to clear a connection once it has served its purpose and the Qt documentation states that a single QNetworkAccessManager instance should serve the entire application. How then, without some complex response routing mechanism in a single shared SLOT can a single QNetworkAccessManager be used to handle multiple, and different network requests?

The solution, it seems, is to not use the QNetworkAccessManager’s finished() signal at all. Instead, the get(), put(), post(), etc. methods of the QNetworkAccessManager return a pointer to a new QNetworkReply that is created to hold the pending response, somewhat analogously to JavaScript’s promises. This QNetworkReply itself has a finished() signal that is emitted when the network response completes. The above example would then look like this:

QNetworkRequest requestOne = makeTypeOneRequest();
QNetworkAccessManager networkManager = getNetworkManagerInstance();
QNetworkReply* reply = networkManager.get(requestOne);
connect(reply, SIGNAL(finished()), this, SLOT(onTypeOneRequestFinished()));

Because the signal is not called with a QNetworkReply parameter, the QObject::sender() method can be used to access the reply and read it’s contents, and delete it. Under this setup, each request is matched with a single handler, and will be cleared when the reply is deleted in the handler.

There is some question of why the QNetworkAccessManager should even have this finished signal when it leads to antipatterns like above. For more reading, this article┬átakes the unequivocal position that this signal shouldn’t exist at all. For my problem, I’m in agreement and will be wrapping the QNetworkAccessManager in a separate layer to hide the unneccessary and misleading signal. If anyone can make a case for the existance of this NetworkAccessManager’s finished() signal, I’d be interested in hearing it.

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn