QNetworkAccessManager不使用訊號/槽機制來獲得QNetworkReply的解決辦法
正在做一個第三方微信企業要介面的專案,JAVA的實現方法比較簡單,網上資料也很多。
但是我需要在C++下實現,微信企業號介面大多都是https的GET/POST方法實現的。
Qt提供了一個封裝好的類QNetworkAccessManager,就不需要使用windows的套接字來程式設計了,也脫離的平臺限制。
上圖是我從http://blog.csdn.net/chenlong12580/article/details/7392766 當中借來得,這位兄弟也很好的演示了QNetworkAccessManager的基本呼叫方法。
但是QNetworkAccessManager是為圖形介面而設計的,當Manager得到finished()訊號時,使用一個SLOT函式去做讀取資料的事,這對於顯示在GUI的資料時,可以說不能再好用了。
但是微信企業號在呼叫任何的介面之前,都需要獲得一個認證,就是access_token,需要使用GET方式來獲得,這使得我需要將獲得access_token的方法封裝成一個函式,以便在每次具體的如傳送微信之前,呼叫它。
貼上程式碼:
void MainWindow::getAccessToken() { QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishedSlot(QNetworkReply*)),Qt::DirectConnection); QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(ACCESS_TOKEN_URL))); }
void MainWindow::finishedSlot(QNetworkReply* reply) { qDebug()<<"start"; QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if(reply->error()==QNetworkReply::NoError) { QByteArray bytes = reply->readAll(); QString rlt =QString::fromUtf8(bytes); accessToken = rlt; } reply->deleteLater(); //ACCESS_FLAG = true; qDebug()<<"finished"; }
以上就是按照官方文件來實現的程式碼片。
如果我再main中直接呼叫
getAccessToken()
的話,程式會直接執行之後的程式碼片,而不會先執行SLOT,因為他們在同一執行緒。
而如果用C++11的 thread.h 用多執行緒呼叫,編譯器也不認QNetworkReply為valid的type,並且多執行緒也不符合程式碼的邏輯,因為主執行緒要等待子執行緒,且只有一個子執行緒。
解決的辦法就是使用QEventLoop:直接上程式碼:
void MainWindow::getAccessToken()
{
QEventLoop temp_loop;
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(ACCESS_TOKEN_URL)));
connect(reply, SIGNAL(finished()), &temp_loop, SLOT(quit()));
temp_loop.exec();
qDebug()<<"start";
QVariant statusCodeV =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl =
reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(reply->error()==QNetworkReply::NoError)
{
QByteArray bytes = reply->readAll();
QString rlt =QString::fromUtf8(bytes);
accessToken = rlt;
qDebug()<<accessToken;
}
reply->deleteLater();
qDebug()<<"finished";
}
通過比較之前的程式碼片,我想應該很好看出不同,不僅within一個方法,而且程式碼也更好讀更短,QNetworkAccessManager的操作封裝在一個函式中了。
這裡還要提一點,按照文件的資料,QNetworkReply的waitForReadyRead()函式,應該也可以完成上述操作,但是我發現不管設定幾秒的等待,函式都無腦回false,看網上說Qt在某個版本後這個函式失效了,我用的是Qt5.4,可能4.x下可以用,應該也是可以實現的的。