How to have one instance of QNetworkAccessManager?

2019-06-02 04:05发布

问题:

There is a similar question but I did not find it useful for my problem.

In Qt documentation, they say:

One QNetworkAccessManager instance should be enough for the whole Qt application.

In my application, I was using QNetworkAccessManager in several places (they may be called simultaneously) and each time I was creating new instance on the stack. After reading that quote I changed my code to have one static QNetworkAccessManager and using it everywhere. After changing it to static member, I always get a warning:

QObject::connect: Cannot connect (null)::aboutToQuit() to QNativeWifiEngine::closeHandle()

In both cases, the code is working without errors, however, that documentation is confusing me a little bit. As the product is going to be commercial, I am taking this issue serious. Should I follow a documentation or avoid a warning? Or do you suggest any other ways?

What if an object on a different thread needs to use a QNetworkAccessManager?


EDIT: adding code

Singleton settings class:

class ConnectionSettingsSingleton
{
   ...
   // constructors = default
   // copy constructor = delete

   public:
      static QNetworkAccessManager networkAccessManager;
}

I use networkAccessManager in different places, but the same way:

QNetworkReply* HttpClient::makeRequest()
{
   switch (this->method) {
   case RequestMethod::GET:
      return ConnectionSettingsSingleton::networkAccessManager.get(this->serverRequest);
   case RequestMethod::POST:
      return ConnectionSettingsSingleton::networkAccessManager.post(this->serverRequest, QJsonDocument(this->data).toJson());
   case RequestMethod::DELETE:
      return ConnectionSettingsSingleton::networkAccessManager.deleteResource(this->serverRequest);
   default:
      return nullptr;
   }
}


EDIT-2

I was kindly using Kuba Ober's answer and at one point I got this warning:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x26f6d0), parent's thread is QThread(0x2b73b8), current thread is QThread(0xa4f20a8)

Reason: I tried to access QNAM from a different thread. Accordingly, I updated my question.

回答1:

You have entire Qt at your disposal. Do not use static variables for this - you don't control their lifetime, and that lifetime is almost always wrong. The QNetworkManager should not exist when QCoreApplication doesn't exist. The warning is not benign at all - it indicates a fundamental design issue. You need to control the lifetime of the manager, and ensure that it doesn't outlive QApplication. The canonical way to manage such global application-tied objects is to keep them as automatic variables in main and use helper functions to access them. The QPointer automatically tracks the lifetime of the object, and thus won't ever be dangling. Thus:

main.h - interface

QNetworkAccessManager *nMgr();

main.cpp - implementation

// This pointer is local to the translation unit, and is an
// implementation detail. It's not used anywhere else.
static QPointer<QNetworkAccessManager> globalManager;

// The global accessor method
QNetworkAccessManager *nMgr() {
  Q_ASSERT(!qApp || QThread::currentThread() == qApp->thread());
  return globalManager;
}

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  QNetworkAccessManager mgr;
  globalManager = &mgr;
  ...
}